Records、Sealed Classes这些新特性:Java真的变简单了吗?
Java近年引入Records、Sealed Classes等新特性,旨在简化开发并增强类型安全。Records极大减少了数据类的样板代码,自动生成构造器、getter、equals等方法,适用于DTO、配置类等场景,但受限于不可变性和不能继承。Sealed Classes通过限制继承范围,使编译器能识别所有可能的子类,结合Pattern Matching可避免遗漏处理分支的情况。这些特性确实提升
Records、Sealed Classes这些新特性:Java真的变简单了吗?

Java这几年新特性挺多的,Records、Sealed Classes、Pattern Matching等等。很多人说Java变简单了,但也有人觉得更复杂了。作为一个一直在用Java的程序员,今天就来聊聊这些新特性,看看是不是真的让Java变简单了。
Records:数据类的简化
Records是Java 14引入的,用来简化数据类。以前写个数据类要写一堆样板代码,现在简单多了。
以前怎么写
// 传统方式:写一堆样板代码
public class User {
private final String name;
private final int age;
private final String email;
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getEmail() {
return email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age &&
Objects.equals(name, user.name) &&
Objects.equals(email, user.email);
}
@Override
public int hashCode() {
return Objects.hash(name, age, email);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
写个简单的数据类,要写50多行代码,大部分都是样板代码。
现在用Records
// Records:一行搞定
public record User(String name, int age, String email) {
}
就这么简单!编译器会自动生成:
- 构造函数
- getter方法(注意,不是
getName(),而是name()) equals()和hashCode()toString()
Records的实际应用
DTO/VO:
// API响应
public record ApiResponse<T>(int code, String message, T data) {
}
// 使用
ApiResponse<User> response = new ApiResponse<>(200, "success", user);
配置类:
public record DatabaseConfig(
String url,
String username,
String password,
int maxConnections
) {
}
// 从配置文件加载
@ConfigurationProperties(prefix = "db")
public record DatabaseConfig(
String url,
String username,
String password,
int maxConnections
) {
}
临时数据结构:
// 方法返回多个值
public record Pair<T, U>(T first, U second) {
}
public Pair<String, Integer> parseUser(String input) {
String[] parts = input.split(",");
return new Pair<>(parts[0], Integer.parseInt(parts[1]));
}
Records的局限性
Records也不是万能的,有些限制:
-
不可变:所有字段都是final的,不能修改。如果需要可变,还是用普通类。
-
不能继承:Records不能继承其他类(除了Record),但可以实现接口。
-
getter命名:getter方法名是
name()而不是getName(),可能和现有代码不兼容。
public record User(String name, int age) {
// 可以添加自定义方法
public boolean isAdult() {
return age >= 18;
}
// 可以自定义构造函数
public User {
if (age < 0) {
throw new IllegalArgumentException("Age must be positive");
}
}
// 可以自定义getter(虽然不推荐)
public String name() {
return name.toUpperCase(); // 但这样会递归调用,编译错误
}
}
我的感受
Records确实让写数据类简单多了,特别是DTO、配置类这些场景。但也不是所有地方都适合用Records,比如需要继承、需要可变性的场景,还是用普通类。
总的来说,Records是个好特性,但不是革命性的,主要是减少样板代码。
Sealed Classes:受控的继承
Sealed Classes是Java 17引入的,用来控制类的继承。以前Java的继承是完全开放的,任何类都可以继承。现在可以限制哪些类能继承。
为什么需要Sealed Classes?
以前写代码,特别是用多态的时候,总担心有子类没处理到:
// 传统方式:完全开放的继承
public abstract class Shape {
public abstract double area();
}
// 问题:任何人都可以继承Shape,可能有未知的子类
public class Circle extends Shape { }
public class Rectangle extends Shape { }
// 可能还有Triangle、Square等等,编译时不知道所有子类
用switch的时候,编译器不知道所有可能的情况:
public double calculateArea(Shape shape) {
if (shape instanceof Circle) {
return ((Circle) shape).getArea();
} else if (shape instanceof Rectangle) {
return ((Rectangle) shape).getArea();
}
// 问题:可能还有其他子类,但编译器不知道,不会警告
return 0;
}
Sealed Classes的解决方案
// Sealed Class:限制继承
public sealed class Shape
permits Circle, Rectangle, Triangle {
public abstract double area();
}
// 只能这三个类继承
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public final class Rectangle extends Shape {
private final double width;
private final double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
public final class Triangle extends Shape {
// ...
}
现在编译器知道所有可能的子类了,用Pattern Matching的时候会有检查:
// Pattern Matching for switch(Java 21+)
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> t.base() * t.height() / 2;
// 编译器知道所有情况都处理了,不需要default
};
}
如果漏了某个子类,编译器会报错,这样就安全多了。
Sealed Classes的实际应用
状态机:
public sealed interface OrderState
permits OrderPending, OrderPaid, OrderShipped, OrderDelivered, OrderCancelled {
}
public final class OrderPending implements OrderState { }
public final class OrderPaid implements OrderState { }
public final class OrderShipped implements OrderState { }
public final class OrderDelivered implements OrderState { }
public final class OrderCancelled implements OrderState { }
表达式树:
public sealed interface Expr
permits ConstantExpr, AddExpr, MultiplyExpr {
}
public record ConstantExpr(int value) implements Expr { }
public record AddExpr(Expr left, Expr right) implements Expr { }
public record MultiplyExpr(Expr left, Expr right) implements Expr { }
我的感受
Sealed Classes是个很好的特性,特别是和Pattern Matching配合使用。它让代码更安全,编译器可以检查完整性。
但这个特性可能对大部分开发者来说,用处不大。主要是框架开发者、库开发者用得比较多。普通业务开发,可能用不到。
Pattern Matching:简化类型判断
Pattern Matching是Java 16引入的,用来简化instanceof的使用。
以前怎么写
// 传统方式:繁琐的类型判断和转换
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.length());
}
现在用Pattern Matching
// Pattern Matching:自动类型转换
if (obj instanceof String str) {
System.out.println(str.length()); // str已经自动转换了
}
Pattern Matching for switch
Java 21支持在switch中用Pattern Matching:
// 传统方式
public String format(Object obj) {
if (obj instanceof Integer i) {
return String.format("Integer: %d", i);
} else if (obj instanceof String s) {
return String.format("String: %s", s);
} else if (obj instanceof Double d) {
return String.format("Double: %f", d);
} else {
return "Unknown";
}
}
// Pattern Matching for switch
public String format(Object obj) {
return switch (obj) {
case Integer i -> String.format("Integer: %d", i);
case String s -> String.format("String: %s", s);
case Double d -> String.format("Double: %f", d);
default -> "Unknown";
};
}
配合Sealed Classes使用
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle(double radius) -> Math.PI * radius * radius; // 直接解构
case Rectangle(double w, double h) -> w * h;
case Triangle(double b, double h) -> b * h / 2;
};
}
我的感受
Pattern Matching确实让代码更简洁,特别是和Sealed Classes配合使用的时候。但单独用的话,提升没那么明显。
总结:Java真的变简单了吗?
这些新特性确实让Java在某些场景下更简洁了:
- Records减少了样板代码
- Sealed Classes + Pattern Matching让代码更安全
- Pattern Matching简化了类型判断
但Java并没有真正"变简单",而是:
- 更精确:可以表达更精确的语义
- 更安全:编译器可以做更多检查
- 更简洁:减少了样板代码
对于新手来说,这些特性可能增加了学习成本。对于老手来说,这些特性让写代码更舒服了。
我的建议是:
- Records:可以多用,特别是DTO、配置类
- Sealed Classes:看场景,框架开发、库开发用得比较多
- Pattern Matching:配合Sealed Classes用,效果最好
不要为了用新特性而用,要根据实际需求来选择。
更多推荐



所有评论(0)