跳转至

如何在父类中为子类自动完成所有的 hashCode 和 equals 实现?这么做有什么问题?

一、场景

父类 Account,子类 UserAccountOrderAccount。希望子类不用自己写 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

@Data
@EqualsAndHashCode(callSuper = true)
public class UserAccount extends Account { ... }

3. 不要让父类 equals 决定子类相等性

相等性应该由最具体的类决定。

五、Effective Java 建议

重写 equals 时遵守约定:自反、对称、传递、一致、非空。

父类实现 equals 后,子类如果加了字段,必须重写 equals 并调用 super

典型坑

父类用 instanceof Account,子类 UserAccount 和父类 Account 只要 id 相同就相等,破坏对称性。