重写 equals() &子类中的 hashCode() ...考虑超级字段

发布于 2024-08-18 06:14:11 字数 3088 浏览 4 评论 0 原文

是否有关于如何覆盖 equals() & 的具体规则? 子类中的hashCode()考虑到超级字段?知道有很多参数:超级字段是私有/公共的,有/没有 getter ...

例如,Netbeans 生成 equals() & hashCode() 不会考虑超级字段...并将

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

返回 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;
    }
    ...  
}

如果您想查看 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;
    }
}

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

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

    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() :

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;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

↙温凉少女 2024-08-25 06:14:11

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

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

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

假设您没有被 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
        }

当然,哈希码很简单:


    @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;     
    }     

不过说实话:NetBeans 不通过调用超类方法来考虑超类字段是怎么回事?

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:


    @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?

雾里花 2024-08-25 06:14:11

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

例子:

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();
}

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

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();
}

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

考虑一个检查字段 xy 的超类,以及检查 xy 的子类z

因此,子类 == 超类 == 子类,其中子类的第一个实例和第二个实例之间的 z 不同,违反了契约的传递部分。

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

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

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

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

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.

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.

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.

萌能量女王 2024-08-25 06:14:11

规则是:

  • 它是自反的:对于任何非空引用值 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,如果对象的 equals 比较中使用的信息没有被修改,则多次调用 x.equals(y) 一致返回 true 或一致返回 false。
  • 对于任何非空引用值 x,x.equals(null) 应返回 false。
  • 每当重写 hashCode 方法时,通常都需要重写该方法,以便维护 hashCode 方法的通用约定,即相等的对象必须具有相等的哈希码(

来自 Object.equals()

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

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

from Object.equals().

So, use the fields needed to fulfill the rules.

地狱即天堂 2024-08-25 06:14:11

好吧,HomoSapiens#hashcode 对于 CPerkins 的回答就足够了。

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

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

@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;
}

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

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;
}

孩子来了。

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;
}

测试...

@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());
}

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

@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.

@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.

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());
}
来世叙缘 2024-08-25 06:14:11

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

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.

夏の忆 2024-08-25 06:14:11

值得注意的是,IDE 自动生成可能已经考虑了超类,只要超类的 equals() 和 hashCode() 还存在即可。也就是说,应该先自动生成super的这两个函数,然后再自动生成child。我在 Intelllj 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;
}

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

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:

@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.

少跟Wǒ拽 2024-08-25 06:14:11

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

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.

狼性发作 2024-08-25 06:14:11

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

如果你的子类向父类添加属性,而父类不是抽象的并且重写了 equals,你就会遇到麻烦。在这种情况下,您应该真正考虑对象组合而不是继承。

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

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.

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.

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

狼性发作 2024-08-25 06:14:11

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

EqualsBuilder.reflectionEquals(this, o);

HashCodeBuilder.reflectionHashCode(this);

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

EqualsBuilder.reflectionEquals(this, o);

HashCodeBuilder.reflectionHashCode(this);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文