引言
Java 17作为继Java 11之后的第二个长期支持(LTS)版本,标志着Java语言在现代化道路上的重要里程碑。本文将深入探讨Java 17中三个最具革命性的特性:Records、Sealed Classes和Pattern Matching,分析它们如何重塑Java的编程范式,并提供实际应用中的最佳实践。
目录
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 OrderState
permits 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 OrderEvent
permits 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 传统类
@Benchmark
public void traditionalClassCreation() {
for (int i = 0; i < 1000000; i++) {
TraditionalPoint point = new TraditionalPoint(i, i * 2);
}
}
@Benchmark
public 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; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof TraditionalPoint other)) return false;
return x == other.x && y == other.y;
}
@Override
public 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<>();
@Override
public 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作为DTO
public 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 PaymentMethod
permits 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方法...
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email, createdAt);
}
@Override
public 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;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public 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;
}
@Override
public double area() {
return width * height;
}
@Override
public 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");
}
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public 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");
}
}
@Override
public double area() {
return width * height;
}
@Override
public 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");
}
}
@Override
public double area() {
double s = perimeter() / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
@Override
public 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 ConfigValue
permits 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开发者更好地理解和应用这些现代化特性,提升代码质量和开发效率。
1375

被折叠的 条评论
为什么被折叠?



