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

结合场景再看三个关注点

  1. 访问器是 skuId() 不是 getSkuId()(惯例不同)。
  2. 紧凑构造器适合校验,不需要重复赋值语句。
  3. equals 按组件,适合值语义。

核心概念与准确模型

1. 声明

record Point(int x, int y) {}

2. 自动成员

  • 私有 final 组件字段
  • 访问器、规范构造器
  • equals/hashCode/toString

3. 限制

  • 不能继承其它类(隐含继承 Record
  • 组件默认不可变(引用指向的对象仍可能可变——浅不可变)
  • 可实现接口、可加静态方法/额外实例方法

边界情况与反直觉行为

  1. 组件是可变 List 时,外部仍可改内容——要拷贝或用不变量集合。
  2. 反射/序列化框架对 record 支持因版本与库而异。
  3. 不适合生命周期复杂、可变状态机的实体根。

常见误区

[!warning] 常见误区:record 替代所有 class 有身份、可变生命周期、丰富不变式维护的对象仍用 class。

工程实践

  1. DTO/命令/事件用 record。
  2. 集合组件用 List.copyOf 护栏。
  3. 与 JSON 映射时确认库对 record 的支持。
  4. 需要建造者时仍可手写静态工厂。

本节总结

  • record = 语言级值载体
  • 浅不可变 + 组件相等
  • 选对场景比滥用重要

自测题

  1. 为什么说 record 是“浅”不可变?
  2. record 能否 extends 自定义父类?
参考答案
  1. 组件引用不可变,但引用目标对象内容可能仍可变。
  2. 不能;只隐式继承 Record 并可实现接口。

延伸阅读与资料来源

资料类型支撑内容
JEP 395 RecordsJEP设计与语义
Record Classes (Oracle)文档用法
创建于 2026/7/15 更新于 2026/7/15