在子类中重写 equals()和 hashCode() ... 考虑超域

如何在 子类中覆盖 equals()hashCode()考虑 超级领域有一个具体的规则吗?知道有许多参数: 超字段是私有/公共的,带有/不带 getter..。

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

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

将返回真实: (

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;
}
}
67507 次浏览

规则如下:

  • 它是自反的: 对于任何非空引用值 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,前提是对象的等于比较中使用的信息没有被修改。
  • 对于任何非空的引用值 x,x.equals (null)应该返回 false。
  • 通常需要在重写 hashCode 方法时重写该方法,以便维护 hashCode 方法的一般约定,该约定指出相等的对象必须具有相等的哈希代码

Object.equals ()

因此,请使用实现规则所需的字段。

我更喜欢使用来自 共同-朗包装平等建造者(和 HashcodeBuilder)来使我的 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();
}

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

一般来说,在子类之间实现 equals 很难保持对称性和可传递性。

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

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

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

因此结果是,子类当然可以考虑 super.equals () ,但是也应该执行自己的 getClass ()检查,以避免上述问题,然后在自己的字段中检查 equals。如果一个类基于超类的特定字段而不仅仅是如果超类返回了等于,那么它就会改变自己的等于行为。

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

但是 很明显,所有重要字段都应该考虑到相等性和散列。

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

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

当然,hashcode 很简单:


@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 为什么不通过调用超类方法来考虑超类字段呢?

听起来您的父(超级)类不会重写 equals。如果是这种情况,那么在子类中重写此方法时,需要比较来自父类的字段。我同意使用 commons EqualsBuiler 是可行的方法,但是您需要小心,不要破坏 equals 契约的对称/转换部分。

如果您的子类向父类添加属性,而父类不是抽象的,并且重写了 equals,那么您将会遇到麻烦。在这个场景中,您应该关注对象组合而不是继承。

我强烈推荐 Joshua Block 在《有效的 Java 》中关于这一点的章节。

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

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

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

测试..。

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

值得注意的是,IDE 自动生成可能已经考虑了超类,只要超类的 equals ()和 hashCode ()已经存在。也就是说,应该先自动生成这两个超级函数,然后再自动生成子函数。下面是《智慧的想法》中的右边例子:

@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 下。

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

RefectionEquals (this,o) ;

RefectionHashCode (this) ;