Java中不可变类的equals判等基于抽象函数(AF)判断内容一致性,只要AF映射到相同抽象值即相等,如Duration(1,2)与Duration(0,62)因AF结果均为62而相等;需重写equals和hashCode,仅比较影响抽象值的字段,采用Objects.equals和Objects.hash,并优先用getClass()确保对称性。

Java 中不可变类的 equals 判等,核心是“内容一致即相等”,不关心内部表示是否相同,只看对外呈现的抽象值是否一样。
基于抽象函数(AF)判断相等
不可变类的等价性通常由抽象函数(Abstraction Function)定义。只要两个对象通过 AF 映射到相同的抽象值,就认为它们相等,哪怕具体字段不同。
- 例如
Duration类:new Duration(1, 2)(1分2秒)和new Duration(0, 62)(62秒)字段不同,但AF(mins, secs) = mins * 60 + secs结果都是 62,因此equals应返回 true - 判等时不直接比所有字段,而是比 AF 的输出结果,或等价地、比所有影响抽象值的字段
观察等价性是默认选择
对不可变类,Java 容器(如 ArrayList、String)普遍采用观察等价性——即外部调用任何公共方法,行为结果完全一致。
- 只要所有 observer 方法(如
getLength()、toString()、size())返回相同结果,就视为相等 - producer 和 creator 方法(如构造器、静态工厂)也应保持一致性;mutator 方法不存在(因不可变),所以无需考虑
重写 equals 时的关键细节
不可变类重写 equals 不必过度复杂,但需守住契约底线:
立即学习“Java免费学习笔记(深入)”;
- 先用
this == obj快速返回,再判null和类型(推荐用getClass() != obj.getClass(),避免子类破坏对称性) - 只比较参与抽象值定义的字段,忽略不影响逻辑语义的辅助字段(如缓存、日志标记)
- 必须同步重写
hashCode(),且参与计算的字段与equals完全一致;建议用Objects.hash(...) - 基本类型用
==,引用类型用Objects.equals(a, b)处理可能为null的情况
String 是典型不可变示例
String 的 equals 就是观察等价性的教科书实现:
- 比较的是字符序列内容,而非内存地址或内部
char[]引用 -
"abc".equals(new String("abc"))返回 true,尽管后者新建了对象 - 常量池优化只是性能手段,不影响判等逻辑;
==偶尔为true是巧合,绝不能依赖


















