记录类是编译器对不可变数据载体的显式支持,自动生成组件访问器(如x()而非getx())和基于字段顺序的确定性hashcode,且equals与hashcode必须配套使用以保证契约一致。

记录类(record)不是语法糖,而是编译器对“不可变数据载体”这一语义的显式支持。它自动生成的属性访问器和 hashCode 方法,不是简单复制粘贴,而是基于字段声明顺序、类型特征与值语义统一设计的。
记录类的属性访问器:不是 getter,是组件访问器
record 中声明的每个字段,都会生成一个同名的**公共、无参、只读方法**,比如 x() 而非 getX()。这叫“组件访问器(component accessor)”,本质是暴露记录的逻辑组成部分,而非传统意义上的封装属性。
- 方法名直接等于字段名,不加 get/set 前缀
- 返回类型与字段声明类型严格一致(含 null 性)
- 没有 setter,也不允许重写该方法——这是不可变契约的一部分
- 即使字段是 private final,访问器也是 public,因为 record 的设计目标就是“透明数据”
hashCode 是按字段顺序组合计算的确定性函数
record 的 hashCode() 不是随机或近似哈希,而是按字段声明顺序,逐个取值、调用对应 hashCode()、再用固定算法累加得出。其行为等价于:Objects.hash(field1, field2, ..., fieldN),但编译器会内联优化,性能更高。
- String 类型字段 → 调用 String.hashCode()
- int 类型字段 → 调用 Integer.hashCode(value)
- List 或嵌套 record 字段 → 递归调用其自身的 hashCode()
- 字段顺序不同,即使内容相同,也属于不同 record 类型,hashCode 不可比(如 record A(int x, int y) 和 record B(int y, int x) 互不兼容)
你不需要、也不应该手动重写 hashCode
record 的 hashCode 与 equals 是配套生成的,二者必须保持一致性。一旦你手动重写 hashCode(),而没同步调整 equals(),就会违反 Object 合约,导致在 HashMap、HashSet 中行为异常——比如对象明明相等却查不到,或重复插入。
- 除非你有特殊场景(如忽略某个字段参与哈希),否则不要覆盖 hashCode()
- 若真需定制,应同时重写 equals(),并确保逻辑完全对称、传递、自反
- 更推荐的做法是:用紧凑构造器做参数校验,或新增静态工厂方法封装逻辑,而不是破坏 record 的自动契约
验证方式很简单:用两个相同字段值的实例对比
写一行测试就能确认是否理解到位:
var p1 = new Point(3, 4); var p2 = new Point(3, 4); System.out.println(p1.equals(p2)); // true System.out.println(p1.hashCode() == p2.hashCode()); // true
只要字段值相同、类型相同、顺序相同,结果就确定且稳定——这才是 record 设计的本意:让“值相等”这件事,开箱即得、无需思考。










