Java Record 与数据载体
record 如何表达不可变数据载体:组件、规范构造器、自动 equals/hashCode/toString 的适用边界。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java Record 与数据载体
这个概念为什么出现
大量类只是“把数据捆在一起”:坐标、金额、命令参数、事件载荷。传统手写 getter/equals/hashCode 噪声大。record 用语言机制声明不可变数据载体。
[!abstract] 一句话理解
record是浅不可变的数据载体:组件构成状态,自动提供访问器、equals/hashCode/toString 与规范构造器,适合做值对象而非富领域实体中心。
最小可运行示例
场景:下单 API 的命令对象
import java.util.Objects;
public record PlaceOrderCommand(String skuId, int quantity) {
public PlaceOrderCommand {
// 紧凑构造器:校验组件
Objects.requireNonNull(skuId, "skuId");
if (skuId.isBlank()) throw new IllegalArgumentException("skuId blank");
if (quantity <= 0) throw new IllegalArgumentException("quantity");
}
public static void main(String[] args) {
var a = new PlaceOrderCommand("S1", 2);
var b = new PlaceOrderCommand("S1", 2);
System.out.println(a);
System.out.println(a.equals(b));
System.out.println(a.skuId() + " x" + a.quantity());
}
}
建议运行:
javac PlaceOrderCommand.java && java PlaceOrderCommand
期望输出:
PlaceOrderCommand[skuId=S1, quantity=2]
true
S1 x2
结合场景再看三个关注点
- 访问器是
skuId()不是getSkuId()(惯例不同)。 - 紧凑构造器适合校验,不需要重复赋值语句。
- equals 按组件,适合值语义。
核心概念与准确模型
1. 声明
record Point(int x, int y) {}
2. 自动成员
- 私有 final 组件字段
- 访问器、规范构造器
equals/hashCode/toString
3. 限制
- 不能继承其它类(隐含继承
Record) - 组件默认不可变(引用指向的对象仍可能可变——浅不可变)
- 可实现接口、可加静态方法/额外实例方法
边界情况与反直觉行为
- 组件是可变 List 时,外部仍可改内容——要拷贝或用不变量集合。
- 反射/序列化框架对 record 支持因版本与库而异。
- 不适合生命周期复杂、可变状态机的实体根。
常见误区
[!warning] 常见误区:record 替代所有 class 有身份、可变生命周期、丰富不变式维护的对象仍用 class。
工程实践
- DTO/命令/事件用 record。
- 集合组件用
List.copyOf护栏。 - 与 JSON 映射时确认库对 record 的支持。
- 需要建造者时仍可手写静态工厂。
本节总结
- record = 语言级值载体
- 浅不可变 + 组件相等
- 选对场景比滥用重要
自测题
- 为什么说 record 是“浅”不可变?
- record 能否 extends 自定义父类?
参考答案
- 组件引用不可变,但引用目标对象内容可能仍可变。
- 不能;只隐式继承 Record 并可实现接口。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JEP 395 Records | JEP | 设计与语义 |
| Record Classes (Oracle) | 文档 | 用法 |