为了账号安全,请及时绑定邮箱和手机立即绑定

在继承层次结构中实现健壮的 equals() 和 hashCode() 方法的正确方法是什么?

在继承层次结构中实现健壮的 equals() 和 hashCode() 方法的正确方法是什么?

米琪卡哇伊 2022-01-19 17:09:13
我有以下抽象Person类:import java.util.Objects;public abstract class Person {    protected String name;    protected int id;    public Person(String name, int id) {        this.name = name;        this.id = id;    }    public abstract String description();    @Override    public boolean equals(Object obj) {        if(this == obj) return true;        if(!(obj instanceof Person)) return false;        return Objects.equals(this.name, ((Person) obj).name) &&                this.id == ((Person) obj).id;    }    @Override    public int hashCode() {        return Objects.hash(this.name, this.id);    }}现在我有一个Person名为的子类Employee:import java.time.LocalDate;import java.util.Objects;public class Employee extends Person {    private double salary;    private LocalDate hireDay;    public Employee(String name, int id, double salary, int year, int month, int day) {        super(name, id);        this.salary = salary;        this.hireDay = LocalDate.of(year, month, day);    }    @Override    public String description() {        return "Employee with a salary of " + this.salary;    }    @Override    public int hashCode() {        return super.hashCode() + Objects.hash(this.salary,this.hireDay);    }}要正确实现 equals 方法,它必须符合以下约定。自反式:x.equals(x) 始终为真对称式:x.equals(y) 等价于 y.equals(x)传递式:x.equals(y) 和 y.equals(z) 蕴含 x.equals(z)是真的当我在子类内部调用超类的equals()方法时,我首先确保所有被比较的对象都是超类的子类。这个问题解决了比较混合类型的问题,并照顾了上面提到的合同。我不再需要使用以下 equals 实现:也就是说,我不再需要显式检查当前对象 ( ) 是否与使用运算符的超类中的方法this属于同一类。objinstance of将该实现放在超类的equals运算符中是否更健壮,或者使用该getClass()方法在子类中使用更明确的测试以符合合同更好?就 hashCode() 方法而言,我对特定于子类的私有实例字段进行哈希处理,然后简单地将其添加到超类中哈希方法的结果中。我找不到任何文档来说明这是否是实现 hashCode() 函数的正确方法,或者在继承层次结构中。我见过人们明确指定自己的哈希函数的代码。如果我的问题过于笼统,我深表歉意,但我尽我所能地问他们,但不会太模棱两可。编辑:我要求 Intellij 实现一个 equals 和 hashcode 方法,它决定使用我上面发布的最后一个实现。那么,在什么情况下我会instance of在超类中使用呢?当我在超类中实现最终的equals方法时会不会是这样,例如仅根据用户ID比较Person对象?
查看完整描述

3 回答

?
qq_花开花谢_0

TA贡献1835条经验 获得超7个赞

以下是我在阅读 Effective Java 2nd Edition 时的笔记:

Equals 必须遵守一般合同:

  • 反身的:对于non-null xx.equals(x) == true

  • 对称:对于non-null x,yx.equals(y) <==> y.equals(x)

  • 传递:对于non-null x,y,zx.equals(y) and y.equals(z) ==> x.equals(z) == true

  • 一致:对于任何非空 x,y: if x.equals(y) == true,如果xy

  • 空:对于非空xx.equals(null) == false

高质量等于方法:

  1. 使用 == 检查参数是否是对该对象的引用 ( x == x)

  2. 使用 instanceof 检查参数是否是正确的类型(也检查null

  3. 将参数转换为正确的类型

  4. 对于类中的每个“重要”字段,检查参数的该字段是否与该对象的相应字段匹配

  5. 完成后,检查是否对称、传递和一致

最后的警告:

  • 覆盖 equals 时始终覆盖 hashCode

  • 不要试图太聪明

  • 不要在 equals 声明中用其他类型替换 Object -> 不值得为了增加复杂性而获得轻微的性能提升

来自 Effective Java 2nd Edition 的Hashcode直接引用

  1. 在名为 result 的 int 变量中存储一些常量非零值,例如 17。

  2. 对于f对象中的每个重要字段(即 equals 方法考虑的每个字段),请执行以下操作:

    • 计算字段的 int 哈希码 c:

    • 将步骤 2.a 中计算的哈希码 c 合并到结果中,如下所示: result = 31 * result + c;

    1. 如果该字段是布尔值,则计算(f ? 1 : 0).

    2. 如果该字段是 byte, char, short, or int, compute (int) f.

    3. 如果该字段是 long, compute (int) (f ^ (f >>> 32)).

    4. 如果该字段是 float, compute Float.floatToIntBits(f).

    5. 如果字段是 a double, compute Double.doubleToLongBits(f),则对结果进行哈希处理long

    6. 如果该字段是对象引用并且此类的 equals 方法通过递归调用比较该字段,则在该字段上equals递归调用hashCode。如果需要更复杂的比较,请为此字段计算“规范表示”并在规范表示上调用 hashCode。如果字段的值为nullreturn 0(或其他一些常数,但 0 是传统的)。

    7. 如果该字段是一个数组,则将其视为每个元素都是一个单独的字段。也就是说,通过递归应用这些规则来计算每个重要元素的哈希码,并在步骤 2.b 中组合这些值。如果数组字段中的每个元素都很重要,则可以使用 1.5 版中添加的 Arrays.hashCode 方法之一。

  3. 返回结果。

  4. 当你写完 hashCode 方法后,问问自己相等的实例是否有相等的哈希码。编写单元测试来验证你的直觉!

所以遵循这些规则:

@Override

public boolean equals(Object obj) {


    if (this == obj) {

        return true;

    }


    if (!(obj instanceof Employee)) {

        return false;

    }

    Employee other = (Employee) obj;


    return super.equals(other) &&

           Double.compare(this.salary, other.salary) == 0 &&

           this.hireDay.equals(other.hireDay);


}

在您的情况下,虽然它似乎id应该已经唯一地标识任何人,所以您应该只使用它进行比较,而不是在任何子类中覆盖它。


查看完整回答
反对 回复 2022-01-19
?
饮歌长啸

TA贡献1951条经验 获得超3个赞

如果你想实现 equals 和 hashcode 方法使用 eclipse 只需右键单击文件转到源并选择 generate equals() & hashcode() 和你需要的字段,如下所示:

//img1.sycdn.imooc.com//61e7d56f000125cc13640766.jpg

//img1.sycdn.imooc.com//61e7d57700018bf013630770.jpg


查看完整回答
反对 回复 2022-01-19
?
陪伴而非守候

TA贡献1757条经验 获得超8个赞

两个人有没有可能永远一样id?它不应该。所以逻辑延伸到Employee类,这意味着在类中实现equals和就足够了。hashCodePerson

此时,由于您只处理一个int,您可以使用Integer.hashCode(id)forhashCode并仅比较 的值equals


查看完整回答
反对 回复 2022-01-19
  • 3 回答
  • 0 关注
  • 137 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信