Java 覆盖子类中的 equals() 和 hashCode() ...考虑超级字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2066917/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:10:53  来源:igfitidea点击:

Overriding equals() & hashCode() in sub classes ... considering super fields

javainheritanceoverridingequalshashcode

提问by wj.

Is there a specific rule on how Overriding equals()& hashCode()in sub classesconsidering super fields?? knowing that there is many parameters : super fields are private/public , with/without getter ...

是否有关于如何在考虑超字段的子类中覆盖equals()&的特定规则?知道有很多参数:超级字段是 private/public ,有/没有 getter ...hashCode()

For instance, Netbeans generated equals() & hashCode() will not consider the super fields ... and

例如,Netbeans 生成的 equals() 和 hashCode() 不会考虑超级字段......

    new HomoSapiens("M", "80", "1.80", "Cammeron", "VeryHot").equals(
    new HomoSapiens("F", "50", "1.50", "Cammeron", "VeryHot"))

will return true :(

将返回真:(

public class Hominidae {

    public String  gender;
    public String  weight;
    public String  height;

    public Hominidae(String gender, String weight, String height) {
        this.gender = gender;
        this.weight = weight;
        this.height = height;
    }
    ... 
}

public class HomoSapiens extends Hominidae {
    public String name;
    public String faceBookNickname;

    public HomoSapiens(String gender, String weight, String height, 
                       String name, String facebookId) {
        super(gender, weight, height);
        this.name = name;
        this.faceBookNickname = facebookId;
    }
    ...  
}

If you want to see the Netbeans generated equals() & hashCode() :

如果您想查看 Netbeans 生成的 equals() 和 hashCode() :

public class Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Hominidae other = (Hominidae) obj;
        if ((this.gender == null) ? (other.gender != null) : !this.gender.equals(other.gender)) {
            return false;
        }
        if ((this.weight == null) ? (other.weight != null) : !this.weight.equals(other.weight)) {
            return false;
        }
        if ((this.height == null) ? (other.height != null) : !this.height.equals(other.height)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + (this.gender != null ? this.gender.hashCode() : 0);
        hash = 37 * hash + (this.weight != null ? this.weight.hashCode() : 0);
        hash = 37 * hash + (this.height != null ? this.height.hashCode() : 0);
        return hash;
    }

}


public class HomoSapiens extends Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final HomoSapiens other = (HomoSapiens) obj;
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        if ((this.faceBookNickname == null) ? (other.faceBookNickname != null) : !this.faceBookNickname.equals(other.faceBookNickname)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);
        return hash;
    }
}

采纳答案by CPerkins

Children should not examine the private members of their parents

孩子不应该检查父母的私人成员

But obviously, all significant fields should be taken into account for equality and hashing.

显然,所有重要的字段都应该考虑到相等和散列。

Fortunately, you you can easily satisfy both rules.

幸运的是,您可以轻松满足这两个规则。

Assuming you're not stuck using the NetBeans-generated equals and hashcode, you can modify Hominidae's equals method to use instanceof comparison rather than class equality, and then use it straightforwardly. Something like this:

假设您没有使用 NetBeans 生成的 equals 和 hashcode,您可以修改 Hominidae 的 equals 方法以使用 instanceof 比较而不是类相等,然后直接使用它。像这样的东西:


    @Override  
    public boolean equals(Object obj) {  
        if (obj == null) { return false; }  
        if (getClass() != obj.getClass()) { return false; }  
        if (! super.equals(obj)) return false;
        else {
           // compare subclass fields
        }

Of course, hashcode is easy:

当然,哈希码很简单:


    @Override     
    public int hashCode() {     
        int hash = super.hashCode();
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);     
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);     
        return hash;     
    }     

Seriously, though: what's up with NetBeans not taking superclass fields into account by calling the superclass methods?

不过,说真的:NetBeans 没有通过调用超类方法来考虑超类字段是怎么回事?

回答by rodrigoap

The rules are:

规则是:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.
  • Is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes
  • 它是自反的:对于任何非空引用值 x,x.equals(x) 应该返回 true。
  • 它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
  • 它是可传递的:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,那么 x.equals(z) 应该返回 true。
  • 它是一致的:对于任何非空引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是对象的 equals 比较中使用的信息没有被修改。
  • 对于任何非空引用值 x,x.equals(null) 应返回 false。
  • 每当重写此方法时,通常都需要重写 hashCode 方法,以维护 hashCode 方法的一般约定,即相等的对象必须具有相等的哈希码

from Object.equals().

来自Object.equals()

So, use the fields needed to fulfill the rules.

因此,请使用满足规则所需的字段。

回答by matt b

I prefer to use EqualsBuilder(and HashcodeBuilder) from the commons-lang packageto make my equals() and hashcode() methods a lot easier to read.

我更喜欢使用commons-lang 包中的EqualsBuilder(和 HashcodeBuilder)来使我的 equals() 和 hashcode() 方法更容易阅读。

Example:

例子:

public boolean equals(Object obj) {
 if (obj == null) { return false; }
 if (obj == this) { return true; }
 if (obj.getClass() != getClass()) {
   return false;
 }
 MyClass rhs = (MyClass) obj;
 return new EqualsBuilder()
             .appendSuper(super.equals(obj))
             .append(field1, rhs.field1)
             .append(field2, rhs.field2)
             .append(field3, rhs.field3)
             .isEquals();
}

回答by Steve Emmerson

Because inheritance breaks encapsulation, subclasses that implement equals() and hashCode() must, necessarily, account for the peculiarities of their superclasses. I've had success encoding calls to the parent class's equals() and hashCode() methods from the subclass's methods.

因为继承破坏了封装,实现 equals() 和 hashCode() 的子类必须考虑到它们超类的特殊性。我已经成功地从子类的方法对父类的 equals() 和 hashCode() 方法进行编码调用。

回答by Yishai

Generally speaking implementing equals across subclasses is hard to keep symmetric and transitive.

一般来说,跨子类实现 equals 很难保持对称和传递。

Consider a superclass that checks for field xand y, and subclass checks for x, yand z.

考虑一个超类,对现场检查xy,和子类的检查xyz

So a Subclass == Superclass == Subclass where z is different between the first instance of Subclass and the second, violating the transitive part of the contract.

所以一个 Subclass == Superclass == Subclass 其中 z 在 Subclass 的第一个实例和第二个实例之间是不同的,违反了合同的传递部分。

This why the typical implementation of equals will check for getClass() != obj.getClass()instead of doing an instanceof. In the above example, if SubClass or Superclass does an instanceof check it would break symmetry.

这就是为什么 equals 的典型实现将检查getClass() != obj.getClass()而不是执行 instanceof 的原因。在上面的示例中,如果 SubClass 或 Superclass 进行 instanceof 检查,它将破坏对称性。

So the upshot is that a subclass can certainly take into account super.equals() but should also do its own getClass() check to avoid the above issues and then check for equals on its own fields in addition. It would be a strange duck of a class that changed its own equals behavior based on specific fields of the superclass rather than just if the superclass returns equals.

所以结果是子类当然可以考虑 super.equals() 但也应该做自己的 getClass() 检查以避免上述问题,然后另外检查自己字段上的 equals。如果一个类根据超类的特定字段而不是仅在超类返回 equals 时改变它自己的 equals 行为,那将是一个奇怪的鸭子。

回答by BigMikeW

It sounds like your parent (super) class doesn't override equals. If this is the case then you need to compare the fields from the parent class when you override this method in the sub-class. I agree that using the commons EqualsBuiler is the way to go but you do need to be careful that you don't break the symmetry/transative portions of the equals contract.

听起来您的父(超级)类不会覆盖 equals。如果是这种情况,那么当您在子类中覆盖此方法时,您需要比较来自父类的字段。我同意使用公共 EqualsBuiler 是可行的方法,但您确实需要小心不要破坏 equals 合同的对称性/传递部分。

If your sub-class adds attributes to the parent class and the parent class isn't abstract and overrides equals you're going to get in to trouble. In this scenario you should really look at object composition instead of inheritance.

如果您的子类向父类添加属性,而父类不是抽象的并且覆盖 equals,那么您将遇到麻烦。在这种情况下,您应该真正着眼于对象组合而不是继承。

I'd strongly recommend the section in Effective Java by Joshua Block on this. It's comprehensive and really well explained.

我强烈推荐 Joshua Block 在 Effective Java 中关于此的部分。它很全面,而且解释得很好。

回答by MattRing

Regarding the accepted @CPerkins answer, I don't think the given equals() code will work reliably, due to the likelihood that the super.equals() method will also check for class equality. A subclass & superclass will not have equal classes.

关于接受的@CPerkins 答案,我认为给定的 equals() 代码不会可靠地工作,因为 super.equals() 方法也可能会检查类是否相等。子类和超类不会有相同的类。

回答by Jin Kwon

Well, HomoSapiens#hashcodewill be enough with CPerkins's answer.

好吧,HomoSapiens#hashcodeCPerkins 的回答就足够了。

@Override     
public int hashCode() {     
    int hash = super.hashCode();
    hash = 89 * hash + Objects.hash(name);     
    hash = 89 * hash + Objects.hash(faceBookNickname);     
    return hash;     
}

If you want those parent's fields(gender, weight, height) in action, one way is creating an actual instance of parent type and use it. Thank God, it's not an abstract class.

如果您希望这些父级的字段( gender, weight, height) 起作用,一种方法是创建父类型的实际实例并使用它。感谢上帝,它不是一个抽象类。

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final HomoSapiens other = (HomoSapiens) obj;
    if (!super.equals(new Hominidae(
        other.gender, other.weight, other.height))) {
         return false;
    }
    if (!Objects.equals(name, other.name)) return false;
    if (!Objects.equals(faceBookNickname, other.faceBookNickname))
        return false;
    return true;
}

I'm adding a way to (I think) solve this. The key point is adding a method loosely checks the equality.

我正在添加一种方法来(我认为)解决这个问题。关键是添加一个方法来松散地检查相等性。

public class Parent {

    public Parent(final String name) {
        super(); this.name = name;
    }

    @Override
    public int hashCode() {
        return hash = 53 * 7 + Objects.hashCode(name);
    }

    @Override
    public boolean equals(final Object obj) {
        return equalsAs(obj) && getClass() == obj.getClass();
    }

    protected boolean equalsAs(final Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Parent other = (Parent) obj;
        if (!Objects.equals(name, other.name)) return false;
        return true;
    }

    private final String name;
}

And here comes the Child.

来了Child

public class Child extends Parent {

    public Child(final String name, final int age) {
        super(name); this.age = age;
    }

    @Override
    public int hashCode() {
        return hash = 31 * super.hashCode() + age;
    }

    @Override
    public boolean equals(final Object obj) {
        return super.equals(obj);
    }

    @Override
    protected boolean equalsAs(final Object obj) {
        if (!super.equalsAs(obj)) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Child other = (Child) obj;
        if (age != other.age) return false;
        return true;
    }

    private final int age;
}

Testing...

测试...

@Test(invocationCount = 128)
public void assertReflective() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    assertTrue(x.equals(x));
    assertEquals(x.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertSymmetric() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(x));
    assertEquals(y.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertTransitive() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    final Child z = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(z));
    assertEquals(y.hashCode(), z.hashCode());
    assertTrue(x.equals(z));
    assertEquals(x.hashCode(), z.hashCode());
}

回答by Ming

It's worth noting that the IDE auto generation maybe has taken into consideration super class,just provided that the equals() and hashCode() of super class exists yet. That is, should auto generate these two functions of super first, and then auto generate of the child. I got below right example under Intellj Idea:

值得注意的是IDE自动生成可能已经考虑了超类,只要超类的equals()和hashCode()存在即可。也就是说,应该先自动生成super这两个函数,然后再自动生成child。我在 Intellj Idea 下得到了正确的例子:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    if (!super.equals(o)) return false;

    TActivityWrapper that = (TActivityWrapper) o;

    return data != null ? data.equals(that.data) : that.data == null;
}

@Override
public int hashCode() {
    int result = super.hashCode();
    result = 31 * result + (data != null ? data.hashCode() : 0);
    return result;
}

The problem happens just when you don't auto generate super's in first. Please check above under Netbeans.

当您不首先自动生成超级时,问题就会发生。请在 Netbeans 下检查以上内容。

回答by Terry H

I believe they now have a method that just does this for you :

我相信他们现在有一种方法可以为您做到这一点:

EqualsBuilder.reflectionEquals(this, o);

EqualsBuilder.reflectionEquals(this, o);

HashCodeBuilder.reflectionHashCode(this);

HashCodeBuilder.reflectionHashCode(this);