应使用 getclass() 而非 instanceof 进行类型检查,确保 equals 对称性;需统一继承链所有类的判断逻辑,配套重写 hashcode 并保证字段一致。

用 getClass() 代替 instanceof 做类型检查
对称性被破坏的根源,常在于父类和子类对“谁算相等”的认定不一致。比如父类用 instanceof Parent 接受子类实例,返回 true;而子类用 instanceof Child 拒绝父类实例,返回 false——结果就是 p.equals(c) == true,但 c.equals(p) == false。
解决方式是:在子类(以及父类)的 equals 方法中,统一使用:
if (obj == null || getClass() != obj.getClass()) return false;- 这确保只有**完全相同的运行时类**才可能进入字段比较阶段
- 看似“拒绝跨类型比较”,实则换来行为可预测、集合操作可靠
子类重写时不要跳过类型校验直接调用 super.equals()
常见错误写法是:先转型再调用 super.equals(that),这会导致父类内部的 getClass() 检查失败(因为传进去的是子类对象,但父类期望同类)。
正确结构是:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 先做
this == obj和getClass() != obj.getClass()判断 - 再强制转型(此时已确保类型一致)
- 然后调用
super.equals(obj)—— 注意传的是原始obj,不是转型后的变量 - 最后比较子类新增字段
避免让父类用 instanceof、子类用 getClass() 混搭
如果父类已经错误地用了 instanceof,子类单方面改成 getClass() 并不能修复对称性——因为 p.equals(c) 仍走父类逻辑返回 true,而 c.equals(p) 却因类型不匹配返回 false。
这时必须:
- 要么统一升级整个继承链,所有类都改用
getClass() - 要么放弃在可继承类中重写
equals,改用 final 类 或 record(其equals天然基于全部字段且不可被覆盖) - 业务上真需跨类型比较(如 DTO 与 Entity),应设计独立的工具方法,而非塞进
equals契约里
配套重写 hashCode 并保持字段一致
对称性不只是 equals 的事。如果子类新增字段参与了 equals 判断,但 hashCode 没包含它,放进 HashMap 后可能出现:map.get(key) 找不到刚 put 进去的对象——表面看是查找失败,根子还是 equals 与 hashCode 不匹配引发的连锁反应。
建议做法:
- 用
Objects.hash(field1, field2, ...)生成哈希码 - 确保
hashCode中使用的字段,与equals中实际比较的字段**严格一致** - 子类重写
hashCode时,不要只调super.hashCode()再叠加——要重新计算完整哈希值
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










