java中equals方法在继承下需严格遵守五契约,优先用getclass()而非instanceof避免对称性破坏,子类重写时须调用super.equals并同步更新hashcode,lombok需显式配置callsuper=true。

Java 中 equals 方法在继承关系下判等,核心在于既要保证逻辑正确,又要守住五个契约(自反、对称、传递、一致、非空),同时避免类型判断陷阱和 hashcode 不一致问题。直接用 instanceof 判断类型看似方便,但在存在多层继承或子类向上转型时,容易破坏对称性和传递性——这是最常被忽略的隐患。
优先用 getClass() 而非 instanceof 做类型检查
使用 instanceof 允许父类实例与子类实例相等(比如 Parent p = new Parent(1); Child c = new Child(1, "a"); p.equals(c) 可能为 true),但反过来 c.equals(p) 却因类型不匹配返回 false,违反对称性。
- 用
obj.getClass() == this.getClass()确保严格同类比较,天然满足对称与传递 - 若业务明确允许“子类实例等于父类实例”(如某些领域模型),需手动补全双向兼容逻辑,但非常规做法
- 不要在父类 equals 中用
instanceof Parent,否则所有子类都会被纳入比较范围,埋下隐患
属性比较必须覆盖整个继承链
子类重写 equals 时,不能只比自己的字段,必须显式调用 super.equals(obj) 来复用父类逻辑——前提是父类已按规范重写过 equals 和 hashCode。
- 父类未重写 equals?子类必须从头实现:先校验 null / 同实例 / 类型,再逐个比较父类字段 + 自己字段
- 字段比较推荐用
Objects.equals(a, b),自动处理 null,比a == null ? b == null : a.equals(b)更简洁安全 - 基本类型用
==,引用类型统一用Objects.equals,避免 NPE
hashCode 必须同步更新且字段严格一致
只要 equals 比较了哪些字段,hashCode 就必须基于完全相同的字段计算。否则放进 HashMap 或 HashSet 会找不到对象。
- 子类重写 hashCode 时,用
Objects.hash(super.hashCode(), field1, field2)或Objects.hash(父类字段..., 子类字段...) - 禁止在 equals 中比较字段 A/B,却在 hashCode 中只用字段 A —— 这会导致逻辑相等的对象 hash 值不同
- IDE 自动生成的 equals/hashCode 通常可靠,但继承场景下务必检查生成逻辑是否包含父类字段
警惕 Lombok @EqualsAndHashCode 的默认行为
Lombok 默认只对当前类字段生成 equals/hashCode,不会自动调用 super.equals,除非显式配置 callSuper = true。
- 正确写法:
@EqualsAndHashCode(callSuper = true) - 若父类没重写 equals/hashCode,加
callSuper = true反而引入 Object 默认行为,导致错误;此时应手动重写或确保父类已规范实现 - 字段列表可显式指定(
of = {"id", "name"}),避免意外包含不参与判等的字段(如临时缓存、数据库 ID)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











