在 Java 开发中,我们常常需要编写大量仅用于封装数据的类:比如 DTO(数据传输对象)、实体模型、配置参数等。传统的 POJO 类充斥着重复的 getter
、setter
、equals
、hashCode
方法,像一场永无止境的仪式。Java 14 引入的 Record 类型,用一行代码终结了这场仪式。本文将带你用 Records 写出极简、安全、高效的代码。
一、传统数据类的痛点
1. 样板代码的泥潭
一个简单的坐标点类需要 50+ 行代码:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// getter、equals、hashCode、toString...
}
2. 隐藏的风险
- 字段可能被错误修改(未正确实现不可变性)
-
equals
方法逻辑错误导致比较失效 - 新增字段时需要修改多个方法
二、Records 的基本语法
1. 定义 Record
用 record
关键字声明一个不可变数据类:
public record Point(int x, int y) {}
编译器会自动生成:
-
final
修饰的字段(x
和y
) - 全参构造方法
-
equals()
、hashCode()
-
toString()
(格式:Point[x=1, y=2]
) -
x()
和y()
访问方法(无get
前缀)
2. 基础使用
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1.x()); // 输出 3
System.out.println(p1.equals(p2)); // 输出 true
三、Records 的进阶特性
1. 自定义构造方法
可以添加紧凑构造方法进行参数校验:
public record User(String name, int age) {
public User {
if (age < 0) throw new IllegalArgumentException("年龄不能为负");
name = name.trim(); // 自动赋值给隐式字段
}
}
2. 添加方法
支持定义静态方法、实例方法:
public record Point(int x, int y) {
public double distance() {
return Math.sqrt(x*x + y*y);
}
public static Point origin() {
return new Point(0, 0);
}
}
3. 实现接口
Records 可以实现接口,但不能继承类:
public record Circle(int radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
四、Records 的适用场景
✅ 推荐使用场景
- 数据传输对象(DTO):JSON 反序列化、API 响应体
- 不可变配置参数:线程池配置、数据库连接参数
- 复合键类型:Map 的复合键、缓存标识
⚠️ 不适用场景
- 需要继承的类
- 需要修改字段的类
- 复杂业务逻辑的领域模型
五、实战对比:Records vs Lombok vs 传统类
1. 代码量对比
类型 | 代码行数 | 可维护性 |
---|---|---|
传统 POJO | 50+ | 低 |
Lombok @Data | 5-10 | 中 |
Java Record | 1 | 高 |
2. 序列化示例(Jackson)
// Record 定义
public record UserResponse(String name, String email) {}
// 自动支持 JSON 序列化/反序列化
String json = new ObjectMapper().writeValueAsString(new UserResponse("Alice", "alice@example.com"));
// 输出:{"name":"Alice","email":"alice@example.com"}
六、常见问题与最佳实践
1. 版本兼容性
- Records 在 Java 16+ 正式支持(Java 14-15 需启用预览功能)
- Spring Framework 6 开始原生支持 Record 类型
2. 设计原则
- 不可变性优先:Records 天然线程安全
- 避免过度扩展:添加业务逻辑时应考虑转为普通类
- 结合模式匹配(Java 21+):
if (obj instanceof Point(int x, int y)) {
System.out.println(x + "," + y);
}
3. 工具链支持
- IDE 自动补全(IntelliJ 2020.3+、Eclipse 2021-03+)
- Jackson 2.12+ 原生支持 Records 反序列化
七、总结
Java Records 不是要取代所有类,而是针对特定场景提供了一种声明式编程范式:
- 极简代码:消灭 95% 的样板代码
- 安全可靠:自动实现不可变性与正确方法
- 语义清晰:明确表达“这是纯数据载体”
当你在项目中遇到“数据胶囊”需求时,不妨尝试用 Records 替代传统 POJO。它或许不能让你少喝一杯咖啡,但一定能让你少写几行 Bug。