← [ BACK_TO_NOTES ]

Java Record 与数据载体

record 如何表达不可变数据载体:组件、规范构造器、自动 equals/hashCode/toString 的适用边界。

#concept #growing #dev #java

[!info] related notes

Java Record 与数据载体

这个概念为什么出现

大量类只是“把数据捆在一起”:坐标、金额、命令参数、事件载荷。传统手写 getter/equals/hashCode 噪声大。record 自 Java 16 正式支持,用语言机制声明浅不可变数据载体。

[!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. 紧凑构造器可校验或规范化参数;正常执行到末尾后编译器才把参数赋给组件字段,不能在其中直接给 this.quantity 等字段赋值。\
  3. 默认 equals 要求同一 record 类且组件相等;数组组件默认按引用身份,不会自动深比较。

核心概念与准确模型

1. 声明

record Point(int x, int y) {}

2. 自动成员

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

3. 限制

  • record 类隐式 final,不能被继承,也不能 extends 其它类(隐含继承 Record)。\
  • 组件默认不可变(引用指向的对象仍可能可变——浅不可变)
  • 可实现接口、可加静态成员/额外实例方法;不能声明额外实例字段。\

边界情况与反直觉行为

  1. 组件是可变 List 时,外部仍可改内容;可在构造器中用 List.copyOf 取得不可修改的浅拷贝,但元素对象仍可能可变,而且 copyOf 拒绝 null 元素。\
  2. 反射/序列化框架对 record 支持因版本与库而异。
  3. 默认浮点组件相等使用 Float.compare/Double.compare 语义,NaN 视为相等、正负零不同;与原始类型 == 不完全一致。
  4. 自动生成的 toString 会包含组件值,敏感数据不应直接依赖它写日志。
  5. 不适合生命周期复杂、可变状态机的实体根。\

常见误区

[!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/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本