重写equals需确保逻辑稳定、可预测且不破坏契约:抽象类用getclass()校验类型、只比自身字段;子类必须调用super.equals()并扩展自有字段;hashcode须严格同步equals所用字段;必要时用canequal模式支持跨层语义相等。

在继承体系中重写 equals,关键不是让所有子类都能和父类相等,而是确保比较逻辑稳定、可预测、不破坏契约。一致性体现在行为可预期、多态调用结果统一、父子类字段参与合理。
抽象父类的 equals 必须只比自身字段
抽象类不能被实例化,但它的引用可能指向子类对象(如 Shape s = new Circle())。因此父类 equals 应:
- 用
getClass() == obj.getClass()判断类型,不用instanceof——避免对称性失效(比如Circle.equals(Shape)为 true,但反过来为 false) - 只比较抽象类自己声明的字段(如
id、name),不访问子类新增属性或可能被重写的 getter 方法 - 判空、自反、类型一致后,逐个用
Objects.equals(a, b)比较字段,自动处理 null 安全
子类重写时必须先调用 super.equals()
子类不是从头写 equals,而是扩展父类逻辑:
- 开头必须写
if (!super.equals(obj)) return false;,否则父类字段不参与比较,违背继承本意 - 类型校验已在父类完成,子类可直接强转:
Circle other = (Circle) obj; - 新增字段继续用
Objects.equals,尤其集合、数组等复杂类型——它内部已处理顺序无关、null 安全等边界
hashCode 必须与 equals 严格同步
equals 和 hashCode 是绑定契约:相等的对象必须有相同哈希值。否则放入 HashSet 或作 HashMap 键时会出错。
- 抽象类的
hashCode只基于其参与equals的字段,例如return Objects.hash(id, name); - 子类重写
hashCode时,应组合父类哈希与自有字段,例如return Objects.hash(super.hashCode(), radius, color); - 漏掉任一参与
equals的字段,就可能造成重复添加或查找失败
CanEqual 模式提供更灵活的可比性控制
当业务上确实需要跨层级语义相等(如 Point 和带颜色的 ColorPoint 在坐标一致时视为相等),getClass() 会过于严格。此时可用 CanEqual 模式:
- 父类定义
protected boolean canEqual(Object other) { return true; } - 子类重写
canEqual,仅当other是本类或其子类实例时返回true -
equals中先调用obj.canEqual(this),再比字段;双方都认可才可比,自然保障对称性 - 注意:
hashCode仍需同步所有参与比较的字段,包括父类和子类的
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











