如何在父类中为子类自动完成所有的 hashCode 和 equals 实现?这么做有什么问题?¶
一、场景¶
父类 Account,子类 UserAccount、OrderAccount。希望子类不用自己写 equals/hashCode。
二、做法¶
让父类实现 equals/hashCode:
public abstract class Account {
private Long id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Account)) return false;
Account a = (Account) o;
return Objects.equals(id, a.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
三、问题¶
1. 违反里氏替换原则¶
父类 equals 只看 id,子类加了 name 字段但没参与 equals:
UserAccount u1 = new UserAccount(1L, "张三");
UserAccount u2 = new UserAccount(1L, "李四");
u1.equals(u2); // true!name 不同却相等
2. instanceof 问题¶
如果子类重写 equals 用 getClass() != o.getClass(),会破坏多态。
3. 双向性问题¶
Account a = new Account(1L);
UserAccount u = new UserAccount(1L, "张三");
a.equals(u); // true
u.equals(a); // 如果子类没重写,也是 true
但 u 有额外字段 a 没有,逻辑上不合理。
四、正确做法¶
1. 子类重写 equals,调用 super¶
@Override
public boolean equals(Object o) {
if (!super.equals(o)) return false;
UserAccount that = (UserAccount) o;
return Objects.equals(name, that.name);
}
2. 用 Lombok¶
3. 不要让父类 equals 决定子类相等性¶
相等性应该由最具体的类决定。
五、Effective Java 建议¶
重写 equals 时遵守约定:自反、对称、传递、一致、非空。
父类实现 equals 后,子类如果加了字段,必须重写 equals 并调用 super。
典型坑
父类用 instanceof Account,子类 UserAccount 和父类 Account 只要 id 相同就相等,破坏对称性。