Java 17新特性深度解读:Records、Sealed Classes与Pattern Matching
引言
Java 17作为继Java 11之后的第二个长期支持(LTS)版本,标志着Java语言在现代化道路上的重要里程碑。本文将深入探讨Java 17中三个最具革命性的特性:Records、Sealed Classes和Pattern Matching,分析它们如何重塑Java的编程范式,并提供实际应用中的最佳实践。
目录
- Records:重新定义数据载体
- Sealed Classes:精确控制类型层次
- Pattern Matching:函数式编程的桥梁
- 三大特性的协同应用
- 性能分析与最佳实践
- 迁移指南与实战案例
Records:重新定义数据载体
深入理解Records的设计哲学
Records的引入不仅仅是为了减少样板代码,更重要的是体现了Java对不可变性和函数式编程的拥抱。Records本质上是一种特殊的类,专门用于建模不可变数据。
// 传统的数据类需要大量样板代码
public final class TraditionalPerson {private final String name;private final int age;private final String email;public TraditionalPerson(String name, int age, String email) {this.name = Objects.requireNonNull(name);this.age = age;this.email = Objects.requireNonNull(email);}// 大量的getter、equals、hashCode、toString方法...
}// 使用Record,一行代码搞定
public record Person(String name, int age, String email) {// 可选:添加验证逻辑public Person {Objects.requireNonNull(name, "Name cannot be null");Objects.requireNonNull(email, "Email cannot be null");if (age < 0) throw new IllegalArgumentException("Age cannot be negative");}
}
Records的高级特性
1. 紧凑构造函数(Compact Constructor)
Records引入了紧凑构造函数的概念,允许在不重复参数列表的情况下添加验证逻辑:
public record BankAccount(String accountNumber, BigDecimal balance, Currency currency) {public BankAccount {// 验证逻辑if (accountNumber == null || accountNumber.trim().isEmpty()) {throw new IllegalArgumentException("Account number cannot be empty");}if (balance.compareTo(BigDecimal.ZERO) < 0) {throw new IllegalArgumentException("Balance cannot be negative");}Objects.requireNonNull(currency, "Currency cannot be null");// 数据标准化accountNumber = accountNumber.trim().toUpperCase();}// 静态工厂方法public static BankAccount createSavingsAccount(String accountNumber, BigDecimal initialDeposit) {return new BankAccount(accountNumber, initialDeposit, Currency.getInstance("USD"));}// 业务方法public BankAccount deposit(BigDecimal amount) {if (amount.compareTo(BigDecimal.ZERO) <= 0) {throw new IllegalArgumentException("Deposit amount must be positive");}return new BankAccount(accountNumber, balance.add(amount), currency);}public BankAccount withdraw(BigDecimal amount) {if (amount.compareTo(balance) > 0) {throw new IllegalArgumentException("Insufficient funds");}return new BankAccount(accountNumber, balance.subtract(amount), currency);}
}
2. Records与泛型的结合
Records完全支持泛型,可以创建类型安全的数据容器:
public record Result<T, E>(T value, E error, boolean isSuccess) {public static <T, E> Result<T, E> success(T value) {return new Result<>(value, null, true);}public static <T, E> Result<T, E> failure(E error) {return new Result<>(null, error, false);}public <U> Result<U, E> map(Function<T, U> mapper) {return isSuccess ? success(mapper.apply(value)) : failure(error);}public <F> Result<T, F> mapError(Function<E, F> mapper) {return isSuccess ? success(value) : failure(mapper.apply(error));}public T orElse(T defaultValue) {return isSuccess ? value : defaultValue;}public T orElseThrow(Function<E, RuntimeException> exceptionMapper) {if (isSuccess) return value;throw exceptionMapper.apply(error);}
}// 使用示例
public class UserService {public Result<User, String> findUserById(Long id) {try {User user = userRepository.findById(id);return user != null ? Result.success(user) : Result.failure("User not found");} catch (Exception e) {return Result.failure("Database error: " + e.getMessage());}}public Result<String, String> processUser(Long userId) {return findUserById(userId).map(user -> "Processed user: " + user.name()).mapError(error -> "Failed to process user: " + error);}
}
Sealed Classes:精确控制类型层次
Sealed Classes的设计动机
Sealed Classes解决了Java长期以来的一个问题:无法精确控制类型层次结构。在传统Java中,一旦声明了一个public接口或抽象类,任何人都可以实现或继承它,这在某些场景下是不希望的。
深入理解Sealed Classes
// 定义一个密封的表达式类型层次
public sealed interface Expression permits Constant, Addition, Multiplication, Variable {
}public record Constant(double value) implements Expression {}public record Variable(String name) implements Expression {}public record Addition(Expression left, Expression right) implements Expression {}public record Multiplication(Expression left, Expression right) implements Expression {}// 表达式求值器
public class ExpressionEvaluator {private final Map<String, Double> variables;public ExpressionEvaluator(Map<String, Double> variables) {this.variables = new HashMap<>(variables);}public double evaluate(Expression expr) {return switch (expr) {case Constant(var value) -> value;case Variable(var name) -> variables.getOrDefault(name, 0.0);case Addition(var left, var right) -> evaluate(left) + evaluate(right);case Multiplication(var left, var right) -> evaluate(left) * evaluate(right);// 编译器保证穷尽性,无需default分支};}// 表达式简化public Expression simplify(Expression expr) {return switch (expr) {case Constant c -> c;case Variable v -> v;case Addition(Constant(0.0), var right) -> simplify(right);case Addition(var left, Constant(0.0)) -> simplify(left);case Addition(Constant(var a), Constant(var b)) -> new Constant(a + b);case Addition(var left, var right) -> new Addition(simplify(left), simplify(right));case Multiplication(Constant(0.0), var right) -> new Constant(0.0);case Multiplication(var left, Constant(0.0)) -> new Constant(0.0);case Multiplication(Constant(1.0), var right) -> simplify(right);case Multiplication(var left, Constant(1.0)) -> simplify(left);case Multiplication(Constant(var a), Constant(var b)) -> new Constant(a * b);case Multiplication(var left, var right) -> new Multiplication(simplify(left), simplify(right));};}
}
Sealed Classes的继承策略
Sealed Classes提供了三种继承策略:
// 1. final - 终止继承链
public final class Circle implements Shape {private final double radius;public Circle(double radius) {this.radius = radius;}public double area() {return Math.PI * radius * radius;}
}// 2. sealed - 继续密封继承
public sealed class Polygon implements Shape permits Triangle, Rectangle, Pentagon {protected final List<Point> vertices;protected Polygon(List<Point> vertices) {this.vertices = List.copyOf(vertices);}
}public final class Triangle extends Polygon {public Triangle(Point a, Point b, Point c) {super(List.of(a, b, c));}public double area() {// 使用海伦公式计算三角形面积Point a = vertices.get(0), b = vertices.get(1), c = vertices.get(2);double sideA = distance(b, c);double sideB = distance(a, c);double sideC = distance(a, b);double s = (sideA + sideB + sideC) / 2;return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC));}private double distance(Point p1, Point p2) {return Math.sqrt(Math.pow(p2.x() - p1.x(), 2) + Math.pow(p2.y() - p1.y(), 2));}
}// 3. non-sealed - 开放继承
public non-sealed class Rectangle extends Polygon {public Rectangle(double width, double height) {super(List.of(new Point(0, 0),new Point(width, 0),new Point(width, height),new Point(0, height)));}public double area() {Point p1 = vertices.get(0), p2 = vertices.get(1), p3 = vertices.get(2);double width = Math.abs(p2.x() - p1.x());double height = Math.abs(p3.y() - p2.y());return width * height;}
}// Rectangle是non-sealed的,所以可以被任意类继承
public class Square extends Rectangle {public Square(double side) {super(side, side);}
}
Pattern Matching:函数式编程的桥梁
Pattern Matching的演进历程
Pattern Matching在Java中的引入是渐进式的:
- Java 14: instanceof的模式匹配(预览)
- Java 16: instanceof的模式匹配(正式)
- Java 17: switch的模式匹配(预览)
- Java 21: switch的模式匹配(正式)
高级Pattern Matching技术
// 复杂的模式匹配示例
public class JsonProcessor {public sealed interface JsonValue permits JsonNull, JsonBoolean, JsonNumber, JsonString, JsonArray, JsonObject {}public record JsonNull() implements JsonValue {}public record JsonBoolean(boolean value) implements JsonValue {}public record JsonNumber(double value) implements JsonValue {}public record JsonString(String value) implements JsonValue {}public record JsonArray(List<JsonValue> elements) implements JsonValue {}public record JsonObject(Map<String, JsonValue> fields) implements JsonValue {}// 使用模式匹配进行JSON处理public String formatJson(JsonValue json, int indent) {return switch (json) {case JsonNull() -> "null";case JsonBoolean(var value) -> String.valueOf(value);case JsonNumber(var value) -> formatNumber(value);case JsonString(var value) -> "\"" + escapeString(value) + "\"";case JsonArray(var elements) -> formatArray(elements, indent);case JsonObject(var fields) -> formatObject(fields, indent);};}private String formatArray(List<JsonValue> elements, int indent) {if (elements.isEmpty()) return "[]";String indentStr = " ".repeat(indent);String nextIndentStr = " ".repeat(indent + 2);return "[\n" + elements.stream().map(elem -> nextIndentStr + formatJson(elem, indent + 2)).collect(Collectors.joining(",\n")) +"\n" + indentStr + "]";}private String formatObject(Map<String, JsonValue> fields, int indent) {if (fields.isEmpty()) return "{}";String indentStr = " ".repeat(indent);String nextIndentStr = " ".repeat(indent + 2);return "{\n" +fields.entrySet().stream().map(entry -> nextIndentStr + "\"" + escapeString(entry.getKey()) + "\": " + formatJson(entry.getValue(), indent + 2)).collect(Collectors.joining(",\n")) +"\n" + indentStr + "}";}// 类型安全的JSON查询public Optional<JsonValue> query(JsonValue json, String path) {String[] parts = path.split("\\.");JsonValue current = json;for (String part : parts) {current = switch (current) {case JsonObject(var fields) -> fields.get(part);case JsonArray(var elements) -> {try {int index = Integer.parseInt(part);yield index >= 0 && index < elements.size() ? elements.get(index) : null;} catch (NumberFormatException e) {yield null;}}default -> null;};if (current == null) return Optional.empty();}return Optional.of(current);}private String formatNumber(double value) {return value == (long) value ? String.valueOf((long) value) : String.valueOf(value);}private String escapeString(String str) {return str.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t");}
}
Guard条件与复杂模式
public class AdvancedPatternMatching {public sealed interface Animal permits Dog, Cat, Bird {}public record Dog(String name, int age, String breed) implements Animal {}public record Cat(String name, int age, boolean isIndoor) implements Animal {}public record Bird(String name, int age, boolean canFly) implements Animal {}// 使用guard条件的模式匹配public String describeAnimal(Animal animal) {return switch (animal) {case Dog(var name, var age, var breed) when age < 1 -> "Puppy " + name + " is a young " + breed;case Dog(var name, var age, var breed) when age > 10 -> "Senior dog " + name + " is an old " + breed;case Dog(var name, var age, var breed) -> name + " is a " + age + "-year-old " + breed;case Cat(var name, var age, true) when age < 1 -> "Kitten " + name + " is an indoor cat";case Cat(var name, var age, false) when age > 12 -> "Senior outdoor cat " + name;case Cat(var name, var age, var isIndoor) -> name + " is a " + age + "-year-old " + (isIndoor ? "indoor" : "outdoor") + " cat";case Bird(var name, var age, true) -> "Flying bird " + name + " (" + age + " years old)";case Bird(var name, var age, false) -> "Flightless bird " + name + " (" + age + " years old)";};}// 嵌套模式匹配public record Owner(String name, Animal pet) {}public String describePetOwner(Owner owner) {return switch (owner) {case Owner(var ownerName, Dog(var petName, var age, "Golden Retriever")) -> ownerName + " owns a Golden Retriever named " + petName;case Owner(var ownerName, Cat(var petName, var age, true)) when age < 2 -> ownerName + " has a young indoor kitten named " + petName;case Owner(var ownerName, var pet) -> ownerName + " owns a pet: " + describeAnimal(pet);};}
}
三大特性的协同应用
构建类型安全的状态机
Records、Sealed Classes和Pattern Matching的结合使用可以创建强大的类型安全状态机:
public class OrderStateMachine {public sealed interface OrderStatepermits Pending, Confirmed, Shipped, Delivered, Cancelled {}public record Pending(LocalDateTime createdAt, BigDecimal amount) implements OrderState {}public record Confirmed(LocalDateTime createdAt, BigDecimal amount,LocalDateTime confirmedAt, String paymentId) implements OrderState {}public record Shipped(LocalDateTime createdAt, BigDecimal amount,LocalDateTime confirmedAt, String paymentId,LocalDateTime shippedAt, String trackingNumber) implements OrderState {}public record Delivered(LocalDateTime createdAt, BigDecimal amount,LocalDateTime confirmedAt, String paymentId,LocalDateTime shippedAt, String trackingNumber,LocalDateTime deliveredAt, String signature) implements OrderState {}public record Cancelled(LocalDateTime createdAt, BigDecimal amount,LocalDateTime cancelledAt, String reason) implements OrderState {}public sealed interface OrderEventpermits ConfirmPayment, ShipOrder, DeliverOrder, CancelOrder {}public record ConfirmPayment(String paymentId) implements OrderEvent {}public record ShipOrder(String trackingNumber) implements OrderEvent {}public record DeliverOrder(String signature) implements OrderEvent {}public record CancelOrder(String reason) implements OrderEvent {}// 状态转换逻辑public Result<OrderState, String> transition(OrderState currentState, OrderEvent event) {return switch (currentState) {case Pending(var createdAt, var amount) -> switch (event) {case ConfirmPayment(var paymentId) -> Result.success(new Confirmed(createdAt, amount, LocalDateTime.now(), paymentId));case CancelOrder(var reason) -> Result.success(new Cancelled(createdAt, amount, LocalDateTime.now(), reason));default -> Result.failure("Invalid transition from Pending state");};case Confirmed(var createdAt, var amount, var confirmedAt, var paymentId) -> switch (event) {case ShipOrder(var trackingNumber) -> Result.success(new Shipped(createdAt, amount, confirmedAt, paymentId,LocalDateTime.now(), trackingNumber));case CancelOrder(var reason) -> Result.success(new Cancelled(createdAt, amount, LocalDateTime.now(), reason));default -> Result.failure("Invalid transition from Confirmed state");};case Shipped(var createdAt, var amount, var confirmedAt, var paymentId,var shippedAt, var trackingNumber) -> switch (event) {case DeliverOrder(var signature) -> Result.success(new Delivered(createdAt, amount, confirmedAt, paymentId,shippedAt, trackingNumber, LocalDateTime.now(), signature));default -> Result.failure("Invalid transition from Shipped state");};case Delivered(var createdAt, var amount, var confirmedAt, var paymentId,var shippedAt, var trackingNumber, var deliveredAt, var signature) ->Result.failure("Order already delivered - no further transitions allowed");case Cancelled(var createdAt, var amount, var cancelledAt, var reason) ->Result.failure("Order cancelled - no further transitions allowed");};}// 获取订单状态描述public String getStateDescription(OrderState state) {return switch (state) {case Pending(var createdAt, var amount) ->"Order pending (Created: %s, Amount: $%.2f)".formatted(createdAt, amount);case Confirmed(var createdAt, var amount, var confirmedAt, var paymentId) ->"Order confirmed (Payment: %s, Amount: $%.2f)".formatted(paymentId, amount);case Shipped(var createdAt, var amount, var confirmedAt, var paymentId,var shippedAt, var trackingNumber) ->"Order shipped (Tracking: %s, Shipped: %s)".formatted(trackingNumber, shippedAt);case Delivered(var createdAt, var amount, var confirmedAt, var paymentId,var shippedAt, var trackingNumber, var deliveredAt, var signature) ->"Order delivered (Delivered: %s, Signature: %s)".formatted(deliveredAt, signature);case Cancelled(var createdAt, var amount, var cancelledAt, var reason) ->"Order cancelled (Reason: %s, Cancelled: %s)".formatted(reason, cancelledAt);};}// 检查是否可以执行特定操作public boolean canExecuteEvent(OrderState state, OrderEvent event) {return transition(state, event).isSuccess();}
}
函数式编程风格的数据处理管道
public class DataProcessingPipeline {public sealed interface ProcessingStep<T, R>permits MapStep, FilterStep, FlatMapStep, ReduceStep {}public record MapStep<T, R>(Function<T, R> mapper) implements ProcessingStep<T, R> {}public record FilterStep<T>(Predicate<T> predicate) implements ProcessingStep<T, T> {}public record FlatMapStep<T, R>(Function<T, Stream<R>> mapper) implements ProcessingStep<T, R> {}public record ReduceStep<T>(T identity, BinaryOperator<T> accumulator) implements ProcessingStep<T, T> {}public sealed interface DataSource<T>permits ListSource, StreamSource, GeneratorSource {}public record ListSource<T>(List<T> data) implements DataSource<T> {}public record StreamSource<T>(Stream<T> stream) implements DataSource<T> {}public record GeneratorSource<T>(Supplier<T> generator, int count) implements DataSource<T> {}// 执行数据处理管道@SuppressWarnings("unchecked")public <T, R> Stream<R> execute(DataSource<T> source, List<ProcessingStep<?, ?>> steps) {Stream<?> currentStream = switch (source) {case ListSource<T>(var data) -> data.stream();case StreamSource<T>(var stream) -> stream;case GeneratorSource<T>(var generator, var count) ->Stream.generate(generator).limit(count);};for (ProcessingStep<?, ?> step : steps) {currentStream = switch (step) {case MapStep<?, ?>(var mapper) -> currentStream.map((Function<Object, Object>) mapper);case FilterStep<?>(var predicate) -> currentStream.filter((Predicate<Object>) predicate);case FlatMapStep<?, ?>(var mapper) -> currentStream.flatMap((Function<Object, Stream<Object>>) mapper);case ReduceStep<?>(var identity, var accumulator) ->Stream.of(currentStream.reduce(identity, (BinaryOperator<Object>) accumulator));};}return (Stream<R>) currentStream;}// 构建器模式public static class PipelineBuilder<T> {private final DataSource<T> source;private final List<ProcessingStep<?, ?>> steps = new ArrayList<>();public PipelineBuilder(DataSource<T> source) {this.source = source;}public <R> PipelineBuilder<R> map(Function<T, R> mapper) {steps.add(new MapStep<>(mapper));return (PipelineBuilder<R>) this;}public PipelineBuilder<T> filter(Predicate<T> predicate) {steps.add(new FilterStep<>(predicate));return this;}public <R> PipelineBuilder<R> flatMap(Function<T, Stream<R>> mapper) {steps.add(new FlatMapStep<>(mapper));return (PipelineBuilder<R>) this;}public <R> Stream<R> execute() {return new DataProcessingPipeline().execute(source, steps);}}public static <T> PipelineBuilder<T> from(List<T> data) {return new PipelineBuilder<>(new ListSource<>(data));}public static <T> PipelineBuilder<T> from(Stream<T> stream) {return new PipelineBuilder<>(new StreamSource<>(stream));}public static <T> PipelineBuilder<T> generate(Supplier<T> generator, int count) {return new PipelineBuilder<>(new GeneratorSource<>(generator, count));}
}// 使用示例
public class PipelineExample {public record Person(String name, int age, String department) {}public void demonstratePipeline() {List<Person> employees = List.of(new Person("Alice", 30, "Engineering"),new Person("Bob", 25, "Marketing"),new Person("Charlie", 35, "Engineering"),new Person("Diana", 28, "Sales"));List<String> engineerNames = DataProcessingPipeline.from(employees).filter(person -> "Engineering".equals(person.department())).map(Person::name).execute().toList();System.out.println("Engineers: " + engineerNames);}
}
性能分析与最佳实践
Records的性能特征
Records在性能方面有以下特点:
- 内存效率:Records生成的类结构紧凑,没有额外的开销
- 方法调用开销:访问器方法可能比直接字段访问略慢,但JIT编译器通常会优化这些调用
- 对象创建:由于不可变性,可能需要更频繁的对象创建
public class RecordsPerformanceAnalysis {// 性能测试:Records vs 传统类@Benchmarkpublic void traditionalClassCreation() {for (int i = 0; i < 1000000; i++) {TraditionalPoint point = new TraditionalPoint(i, i * 2);}}@Benchmarkpublic void recordCreation() {for (int i = 0; i < 1000000; i++) {Point point = new Point(i, i * 2);}}// 传统类public static final class TraditionalPoint {private final int x, y;public TraditionalPoint(int x, int y) {this.x = x;this.y = y;}public int getX() { return x; }public int getY() { return y; }@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (!(obj instanceof TraditionalPoint other)) return false;return x == other.x && y == other.y;}@Overridepublic int hashCode() {return Objects.hash(x, y);}}// Record类public record Point(int x, int y) {}// 性能优化建议public record OptimizedPerson(String name, int age) {// 缓存hashCode以提高性能private static final Map<OptimizedPerson, Integer> hashCodeCache =new ConcurrentHashMap<>();@Overridepublic int hashCode() {return hashCodeCache.computeIfAbsent(this,person -> Objects.hash(person.name(), person.age()));}// 对于频繁使用的计算,可以添加缓存public String displayName() {return name + " (" + age + ")";}}
}
Sealed Classes的性能影响
Sealed Classes主要影响编译时性能和运行时的模式匹配效率:
public class SealedClassesPerformance {public sealed interface Operation permits Add, Subtract, Multiply, Divide {}public record Add(double a, double b) implements Operation {}public record Subtract(double a, double b) implements Operation {}public record Multiply(double a, double b) implements Operation {}public record Divide(double a, double b) implements Operation {}// 高效的模式匹配实现public double calculate(Operation op) {return switch (op) {case Add(var a, var b) -> a + b;case Subtract(var a, var b) -> a - b;case Multiply(var a, var b) -> a * b;case Divide(var a, var b) -> b != 0 ? a / b : Double.NaN;};}// 与传统多态方法的性能对比public sealed interface PolymorphicOperation permits PolyAdd, PolySubtract, PolyMultiply, PolyDivide {double execute();}public record PolyAdd(double a, double b) implements PolymorphicOperation {public double execute() { return a + b; }}public record PolySubtract(double a, double b) implements PolymorphicOperation {public double execute() { return a - b; }}public record PolyMultiply(double a, double b) implements PolymorphicOperation {public double execute() { return a * b; }}public record PolyDivide(double a, double b) implements PolymorphicOperation {public double execute() { return b != 0 ? a / b : Double.NaN; }}
}
最佳实践指南
1. Records使用最佳实践
public class RecordsBestPractices {// ✅ 好的实践:使用Records作为DTOpublic record UserDTO(Long id, String username, String email, LocalDateTime createdAt) {public UserDTO {Objects.requireNonNull(username, "Username cannot be null");Objects.requireNonNull(email, "Email cannot be null");if (username.trim().isEmpty()) {throw new IllegalArgumentException("Username cannot be empty");}}}// ✅ 好的实践:Records与Builder模式结合public record ComplexConfiguration(String host,int port,boolean ssl,Duration timeout,Map<String, String> properties) {public ComplexConfiguration {Objects.requireNonNull(host);if (port <= 0 || port > 65535) {throw new IllegalArgumentException("Invalid port: " + port);}Objects.requireNonNull(timeout);properties = Map.copyOf(properties); // 防御性复制}public static Builder builder() {return new Builder();}public static class Builder {private String host = "localhost";private int port = 8080;private boolean ssl = false;private Duration timeout = Duration.ofSeconds(30);private Map<String, String> properties = new HashMap<>();public Builder host(String host) {this.host = host;return this;}public Builder port(int port) {this.port = port;return this;}public Builder ssl(boolean ssl) {this.ssl = ssl;return this;}public Builder timeout(Duration timeout) {this.timeout = timeout;return this;}public Builder property(String key, String value) {this.properties.put(key, value);return this;}public ComplexConfiguration build() {return new ComplexConfiguration(host, port, ssl, timeout, properties);}}}// ❌ 避免:Records包含可变对象而不进行防御性复制public record BadRecord(List<String> items) {} // 危险:外部可以修改list// ✅ 正确:进行防御性复制public record GoodRecord(List<String> items) {public GoodRecord {items = List.copyOf(items); // 创建不可变副本}}
}
2. Sealed Classes使用最佳实践
public class SealedClassesBestPractices {// ✅ 好的实践:使用Sealed Classes建模有限状态集public sealed interface PaymentMethodpermits CreditCard, DebitCard, PayPal, BankTransfer {}public record CreditCard(String number, String holderName, YearMonth expiry)implements PaymentMethod {public CreditCard {Objects.requireNonNull(number);Objects.requireNonNull(holderName);Objects.requireNonNull(expiry);if (expiry.isBefore(YearMonth.now())) {throw new IllegalArgumentException("Card expired");}}}public record DebitCard(String number, String holderName, String pin)implements PaymentMethod {public DebitCard {Objects.requireNonNull(number);Objects.requireNonNull(holderName);Objects.requireNonNull(pin);}}public record PayPal(String email) implements PaymentMethod {public PayPal {Objects.requireNonNull(email);if (!email.contains("@")) {throw new IllegalArgumentException("Invalid email");}}}public record BankTransfer(String accountNumber, String routingNumber)implements PaymentMethod {public BankTransfer {Objects.requireNonNull(accountNumber);Objects.requireNonNull(routingNumber);}}// 类型安全的支付处理public class PaymentProcessor {public Result<String, String> processPayment(PaymentMethod method, BigDecimal amount) {return switch (method) {case CreditCard(var number, var holder, var expiry) ->processCreditCard(number, holder, expiry, amount);case DebitCard(var number, var holder, var pin) ->processDebitCard(number, holder, pin, amount);case PayPal(var email) ->processPayPal(email, amount);case BankTransfer(var account, var routing) ->processBankTransfer(account, routing, amount);};}private Result<String, String> processCreditCard(String number, String holder,YearMonth expiry, BigDecimal amount) {// 信用卡处理逻辑return Result.success("Credit card payment processed: " + amount);}private Result<String, String> processDebitCard(String number, String holder,String pin, BigDecimal amount) {// 借记卡处理逻辑return Result.success("Debit card payment processed: " + amount);}private Result<String, String> processPayPal(String email, BigDecimal amount) {// PayPal处理逻辑return Result.success("PayPal payment processed: " + amount);}private Result<String, String> processBankTransfer(String account, String routing,BigDecimal amount) {// 银行转账处理逻辑return Result.success("Bank transfer processed: " + amount);}}
}
迁移指南与实战案例
从传统代码迁移到Java 17新特性
1. 识别迁移候选
// 迁移前:传统的数据类
public class LegacyUser {private final Long id;private final String username;private final String email;private final LocalDateTime createdAt;public LegacyUser(Long id, String username, String email, LocalDateTime createdAt) {this.id = id;this.username = username;this.email = email;this.createdAt = createdAt;}// 大量的getter方法...public Long getId() { return id; }public String getUsername() { return username; }public String getEmail() { return email; }public LocalDateTime getCreatedAt() { return createdAt; }// equals、hashCode、toString方法...@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof LegacyUser that)) return false;return Objects.equals(id, that.id) &&Objects.equals(username, that.username) &&Objects.equals(email, that.email) &&Objects.equals(createdAt, that.createdAt);}@Overridepublic int hashCode() {return Objects.hash(id, username, email, createdAt);}@Overridepublic String toString() {return "LegacyUser{" +"id=" + id +", username='" + username + '\'' +", email='" + email + '\'' +", createdAt=" + createdAt +'}';}
}// 迁移后:使用Record
public record User(Long id, String username, String email, LocalDateTime createdAt) {public User {Objects.requireNonNull(username, "Username cannot be null");Objects.requireNonNull(email, "Email cannot be null");Objects.requireNonNull(createdAt, "Created date cannot be null");if (username.trim().isEmpty()) {throw new IllegalArgumentException("Username cannot be empty");}if (!email.contains("@")) {throw new IllegalArgumentException("Invalid email format");}}// 可以添加业务方法public boolean isRecentlyCreated() {return createdAt.isAfter(LocalDateTime.now().minusDays(7));}public User withEmail(String newEmail) {return new User(id, username, newEmail, createdAt);}
}
2. 重构类型层次结构
// 迁移前:传统的多态设计
public abstract class LegacyShape {public abstract double area();public abstract double perimeter();
}public class LegacyCircle extends LegacyShape {private final double radius;public LegacyCircle(double radius) {this.radius = radius;}@Overridepublic double area() {return Math.PI * radius * radius;}@Overridepublic double perimeter() {return 2 * Math.PI * radius;}public double getRadius() { return radius; }
}public class LegacyRectangle extends LegacyShape {private final double width;private final double height;public LegacyRectangle(double width, double height) {this.width = width;this.height = height;}@Overridepublic double area() {return width * height;}@Overridepublic double perimeter() {return 2 * (width + height);}public double getWidth() { return width; }public double getHeight() { return height; }
}// 传统的处理方式
public class LegacyShapeProcessor {public String describeShape(LegacyShape shape) {if (shape instanceof LegacyCircle circle) {return "Circle with radius " + circle.getRadius();} else if (shape instanceof LegacyRectangle rectangle) {return "Rectangle " + rectangle.getWidth() + "x" + rectangle.getHeight();} else {return "Unknown shape";}}
}// 迁移后:使用Sealed Classes + Records + Pattern Matching
public sealed interface Shape permits Circle, Rectangle, Triangle {double area();double perimeter();
}public record Circle(double radius) implements Shape {public Circle {if (radius <= 0) {throw new IllegalArgumentException("Radius must be positive");}}@Overridepublic double area() {return Math.PI * radius * radius;}@Overridepublic double perimeter() {return 2 * Math.PI * radius;}
}public record Rectangle(double width, double height) implements Shape {public Rectangle {if (width <= 0 || height <= 0) {throw new IllegalArgumentException("Width and height must be positive");}}@Overridepublic double area() {return width * height;}@Overridepublic double perimeter() {return 2 * (width + height);}public boolean isSquare() {return Math.abs(width - height) < 0.001;}
}public record Triangle(double a, double b, double c) implements Shape {public Triangle {if (a <= 0 || b <= 0 || c <= 0) {throw new IllegalArgumentException("All sides must be positive");}if (a + b <= c || a + c <= b || b + c <= a) {throw new IllegalArgumentException("Invalid triangle sides");}}@Overridepublic double area() {double s = perimeter() / 2;return Math.sqrt(s * (s - a) * (s - b) * (s - c));}@Overridepublic double perimeter() {return a + b + c;}
}// 现代化的处理方式
public class ModernShapeProcessor {public String describeShape(Shape shape) {return switch (shape) {case Circle(var radius) -> "Circle with radius %.2f".formatted(radius);case Rectangle(var width, var height) when Math.abs(width - height) < 0.001 ->"Square with side %.2f".formatted(width);case Rectangle(var width, var height) ->"Rectangle %.2fx%.2f".formatted(width, height);case Triangle(var a, var b, var c) ->"Triangle with sides %.2f, %.2f, %.2f".formatted(a, b, c);};}public String getShapeCategory(Shape shape) {return switch (shape) {case Circle c -> "Curved shape";case Rectangle r when r.isSquare() -> "Regular polygon";case Rectangle r -> "Quadrilateral";case Triangle t -> "Polygon";};}public double calculateTotalArea(List<Shape> shapes) {return shapes.stream().mapToDouble(Shape::area).sum();}
}
实战案例:构建一个类型安全的配置系统
让我们通过一个完整的实战案例来展示Java 17新特性的综合应用:
// 配置系统的核心类型定义
public class ConfigurationSystem {// 使用Sealed Interface定义配置值类型public sealed interface ConfigValuepermits StringValue, IntValue, BooleanValue, ListValue, ObjectValue {}public record StringValue(String value) implements ConfigValue {public StringValue {Objects.requireNonNull(value, "String value cannot be null");}}public record IntValue(int value) implements ConfigValue {}public record BooleanValue(boolean value) implements ConfigValue {}public record ListValue(List<ConfigValue> values) implements ConfigValue {public ListValue {values = List.copyOf(values); // 防御性复制}}public record ObjectValue(Map<String, ConfigValue> properties) implements ConfigValue {public ObjectValue {properties = Map.copyOf(properties); // 防御性复制}}// 配置验证结果public sealed interface ValidationResult permits Valid, Invalid {}public record Valid() implements ValidationResult {}public record Invalid(List<String> errors) implements ValidationResult {public Invalid {errors = List.copyOf(errors);}public Invalid(String error) {this(List.of(error));}}// 配置解析器public class ConfigParser {public Result<ConfigValue, String> parseValue(String input) {input = input.trim();// 使用模式匹配进行解析return switch (input) {case String s when s.equals("true") || s.equals("false") ->Result.success(new BooleanValue(Boolean.parseBoolean(s)));case String s when s.matches("-?\\d+") -> {try {yield Result.success(new IntValue(Integer.parseInt(s)));} catch (NumberFormatException e) {yield Result.failure("Invalid integer: " + s);}}case String s when s.startsWith("\"") && s.endsWith("\"") ->Result.success(new StringValue(s.substring(1, s.length() - 1)));case String s when s.startsWith("[") && s.endsWith("]") ->parseList(s);case String s when s.startsWith("{") && s.endsWith("}") ->parseObject(s);default -> Result.success(new StringValue(input));};}private Result<ConfigValue, String> parseList(String input) {// 简化的列表解析逻辑String content = input.substring(1, input.length() - 1).trim();if (content.isEmpty()) {return Result.success(new ListValue(List.of()));}String[] elements = content.split(",");List<ConfigValue> values = new ArrayList<>();for (String element : elements) {Result<ConfigValue, String> result = parseValue(element.trim());if (!result.isSuccess()) {return result;}values.add(result.value());}return Result.success(new ListValue(values));}private Result<ConfigValue, String> parseObject(String input) {// 简化的对象解析逻辑String content = input.substring(1, input.length() - 1).trim();if (content.isEmpty()) {return Result.success(new ObjectValue(Map.of()));}Map<String, ConfigValue> properties = new HashMap<>();String[] pairs = content.split(",");for (String pair : pairs) {String[] keyValue = pair.split(":", 2);if (keyValue.length != 2) {return Result.failure("Invalid key-value pair: " + pair);}String key = keyValue[0].trim();if (key.startsWith("\"") && key.endsWith("\"")) {key = key.substring(1, key.length() - 1);}Result<ConfigValue, String> valueResult = parseValue(keyValue[1].trim());if (!valueResult.isSuccess()) {return valueResult;}properties.put(key, valueResult.value());}return Result.success(new ObjectValue(properties));}}// 配置验证器public class ConfigValidator {public ValidationResult validate(ConfigValue value, ConfigSchema schema) {return switch (value) {case StringValue(var str) -> validateString(str, schema);case IntValue(var num) -> validateInt(num, schema);case BooleanValue(var bool) -> validateBoolean(bool, schema);case ListValue(var list) -> validateList(list, schema);case ObjectValue(var obj) -> validateObject(obj, schema);};}private ValidationResult validateString(String value, ConfigSchema schema) {List<String> errors = new ArrayList<>();if (schema.minLength() > 0 && value.length() < schema.minLength()) {errors.add("String too short: minimum length is " + schema.minLength());}if (schema.maxLength() > 0 && value.length() > schema.maxLength()) {errors.add("String too long: maximum length is " + schema.maxLength());}if (schema.pattern() != null && !value.matches(schema.pattern())) {errors.add("String does not match pattern: " + schema.pattern());}return errors.isEmpty() ? new Valid() : new Invalid(errors);}private ValidationResult validateInt(int value, ConfigSchema schema) {List<String> errors = new ArrayList<>();if (schema.minimum() != null && value < schema.minimum()) {errors.add("Value too small: minimum is " + schema.minimum());}if (schema.maximum() != null && value > schema.maximum()) {errors.add("Value too large: maximum is " + schema.maximum());}return errors.isEmpty() ? new Valid() : new Invalid(errors);}private ValidationResult validateBoolean(boolean value, ConfigSchema schema) {return new Valid(); // 布尔值通常不需要额外验证}private ValidationResult validateList(List<ConfigValue> values, ConfigSchema schema) {List<String> errors = new ArrayList<>();if (schema.minItems() > 0 && values.size() < schema.minItems()) {errors.add("Too few items: minimum is " + schema.minItems());}if (schema.maxItems() > 0 && values.size() > schema.maxItems()) {errors.add("Too many items: maximum is " + schema.maxItems());}// 验证每个元素if (schema.itemSchema() != null) {for (int i = 0; i < values.size(); i++) {ValidationResult itemResult = validate(values.get(i), schema.itemSchema());if (itemResult instanceof Invalid(var itemErrors)) {for (String error : itemErrors) {errors.add("Item " + i + ": " + error);}}}}return errors.isEmpty() ? new Valid() : new Invalid(errors);}private ValidationResult validateObject(Map<String, ConfigValue> properties, ConfigSchema schema) {List<String> errors = new ArrayList<>();// 检查必需的属性for (String required : schema.requiredProperties()) {if (!properties.containsKey(required)) {errors.add("Missing required property: " + required);}}// 验证每个属性for (Map.Entry<String, ConfigValue> entry : properties.entrySet()) {String propertyName = entry.getKey();ConfigValue propertyValue = entry.getValue();ConfigSchema propertySchema = schema.propertySchemas().get(propertyName);if (propertySchema != null) {ValidationResult propertyResult = validate(propertyValue, propertySchema);if (propertyResult instanceof Invalid(var propertyErrors)) {for (String error : propertyErrors) {errors.add("Property " + propertyName + ": " + error);}}}}return errors.isEmpty() ? new Valid() : new Invalid(errors);}}// 配置模式定义public record ConfigSchema(String type,Integer minLength,Integer maxLength,String pattern,Integer minimum,Integer maximum,Integer minItems,Integer maxItems,ConfigSchema itemSchema,List<String> requiredProperties,Map<String, ConfigSchema> propertySchemas) {public ConfigSchema {requiredProperties = requiredProperties != null ? List.copyOf(requiredProperties) : List.of();propertySchemas = propertySchemas != null ? Map.copyOf(propertySchemas) : Map.of();}// 便利的构造方法public static ConfigSchema stringSchema(int minLength, int maxLength, String pattern) {return new ConfigSchema("string", minLength, maxLength, pattern, null, null,null, null, null, null, null);}public static ConfigSchema intSchema(int minimum, int maximum) {return new ConfigSchema("integer", null, null, null, minimum, maximum,null, null, null, null, null);}public static ConfigSchema booleanSchema() {return new ConfigSchema("boolean", null, null, null, null, null,null, null, null, null, null);}public static ConfigSchema listSchema(ConfigSchema itemSchema, int minItems, int maxItems) {return new ConfigSchema("array", null, null, null, null, null,minItems, maxItems, itemSchema, null, null);}public static ConfigSchema objectSchema(List<String> required, Map<String, ConfigSchema> properties) {return new ConfigSchema("object", null, null, null, null, null,null, null, null, required, properties);}}
}// 使用示例
public class ConfigurationExample {public void demonstrateConfigSystem() {var parser = new ConfigurationSystem().new ConfigParser();var validator = new ConfigurationSystem().new ConfigValidator();// 解析配置String configText = """{"host": "localhost","port": 8080,"ssl": true,"features": ["auth", "logging", "metrics"]}""";Result<ConfigValue, String> parseResult = parser.parseValue(configText);if (parseResult.isSuccess()) {ConfigValue config = parseResult.value();// 定义验证模式ConfigSchema schema = ConfigSchema.objectSchema(List.of("host", "port"),Map.of("host", ConfigSchema.stringSchema(1, 255, null),"port", ConfigSchema.intSchema(1, 65535),"ssl", ConfigSchema.booleanSchema(),"features", ConfigSchema.listSchema(ConfigSchema.stringSchema(1, 50, null), 0, 10)));// 验证配置ValidationResult validationResult = validator.validate(config, schema);switch (validationResult) {case Valid() -> System.out.println("Configuration is valid");case Invalid(var errors) -> {System.out.println("Configuration errors:");errors.forEach(error -> System.out.println(" - " + error));}}} else {System.out.println("Parse error: " + parseResult.error());}}
}
总结与展望
Java 17通过Records、Sealed Classes和Pattern Matching三大特性,为Java语言带来了革命性的变化:
核心价值
- 简洁性:Records大幅减少了样板代码,让开发者专注于业务逻辑
- 类型安全:Sealed Classes提供了精确的类型控制,减少了运行时错误
- 表达力:Pattern Matching使代码更加直观和易读
- 函数式编程支持:这些特性为Java引入了更多函数式编程概念
最佳实践总结
-
Records适用场景:
- 数据传输对象(DTO)
- 值对象(Value Objects)
- 不可变数据容器
- API响应模型
-
Sealed Classes适用场景:
- 有限状态集合
- 代数数据类型
- 领域模型
- API设计中的封闭类型层次
-
Pattern Matching适用场景:
- 复杂的条件逻辑
- 数据解构
- 类型安全的分支处理
- 函数式编程风格的代码
未来展望
Java语言的发展方向明确指向更现代、更简洁、更安全的编程体验。随着Project Amber的持续推进,我们可以期待:
- 更强大的Pattern Matching:支持更复杂的模式和guard条件
- Value Classes:提供更高效的值类型支持
- String Templates:类型安全的字符串插值
- Primitive Classes:统一原始类型和对象类型
Java 17的这些新特性不仅提升了开发效率,更重要的是为Java生态系统的现代化奠定了基础。对于Java开发者而言,掌握这些特性不仅是技术要求,更是拥抱Java未来发展的必要准备。
参考资料
- JEP 395: Records
- JEP 409: Sealed Classes
- JEP 406: Pattern Matching for switch (Preview)
- Java 17 Documentation
- Oracle Java 17 Release Notes
- Inside Java Podcast: Records and Sealed Classes
- Project Amber
- Java Language Specification - Records
- Java Language Specification - Sealed Classes
- Effective Java 3rd Edition - Joshua Bloch
本文深入探讨了Java 17中Records、Sealed Classes和Pattern Matching三大核心特性,通过丰富的代码示例和实战案例,展示了这些特性如何改变Java编程范式。希望本文能帮助Java开发者更好地理解和应用这些现代化特性,提升代码质量和开发效率。