71

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

10 に答える 10

69

子供は親の非公開メンバーを調べてはならない

しかし明らかに、すべての重要なフィールドは、等価性とハッシングのために考慮されるべきです。

幸いなことに、両方のルールを簡単に満たすことができます。

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 がスーパークラス メソッドを呼び出すことによってスーパークラス フィールドを考慮しないのはなぜでしょうか?

于 2010-01-14T20:41:54.910 に答える
23

私はcommons-langパッケージのEqualsBuilder(および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();
}
于 2010-01-14T19:42:57.900 に答える
8

一般に、サブクラス全体で equals を実装することは、対称性と推移性を維持するのが困難です。

フィールドxとをチェックするスーパークラスと、とyをチェックするサブクラスを考えてみましょう。xyz

したがって、Subclass == Superclass == Subclass の z は Subclass の最初のインスタンスと 2 番目のインスタンスの間で異なり、契約の推移的な部分に違反します。

getClass() != obj.getClass()これが、equals の典型的な実装がinstanceof を実行する代わりにチェックする理由です。上記の例では、SubClass または Superclass が instanceof チェックを行うと、対称性が崩れます。

つまり、サブクラスは確実に super.equals() を考慮に入れることができますが、上記の問題を回避するために独自の getClass() チェックを行い、さらに独自のフィールドで equals をチェックする必要があります。スーパークラスが equals を返す場合だけでなく、スーパークラスの特定のフィールドに基づいて独自の equals 動作を変更したクラスの奇妙なアヒルです。

于 2010-01-14T20:02:07.130 に答える
6

ルールは次のとおりです。

  • これは再帰的です。null でない参照値 x に対して、x.equals(x) は true を返す必要があります。
  • これは対称的です: null 以外の参照値 x および y について、y.equals(x) が true を返す場合に限り、x.equals(y) は true を返す必要があります。
  • これは推移的です: null 以外の参照値 x、y、および z に対して、x.equals(y) が true を返し、y.equals(z) が true を返す場合、x.equals(z) は true を返す必要があります。
  • 一貫性があります。null 以外の参照値 x および y に対して、x.equals(y) の複数の呼び出しは一貫して true を返すか、一貫して false を返します。ただし、オブジェクトの equals 比較で使用される情報が変更されていない場合に限ります。
  • null 以外の参照値 x の場合、x.equals(null) は false を返す必要があります。
  • 通常、このメソッドがオーバーライドされるときは常に、hashCode メソッドをオーバーライドする必要があります。これは、等しいオブジェクトには等しいハッシュ コードが必要であると述べている hashCode メソッドの一般的な契約を維持するためです。

Object.equals()から。

したがって、ルールを満たすために必要なフィールドを使用してください。

于 2010-01-14T19:33:06.737 に答える
3

まあ、HomoSapiens#hashcodeCPerkinsの答えで十分でしょう。

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

genderこれらの親のフィールド ( 、weight、 ) を動作させたい場合height、1 つの方法は、親タイプの実際のインスタンスを作成して使用することです。神に感謝します。これは抽象クラスではありません。

@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());
}
于 2016-08-20T15:25:13.370 に答える
2

継承はカプセル化を破るため、equals() と hashCode() を実装するサブクラスは、必然的に、そのスーパークラスの特性を考慮する必要があります。サブクラスのメソッドから親クラスの equals() および hashCode() メソッドへの呼び出しをエンコードすることに成功しました。

于 2010-01-14T19:48:09.780 に答える
2

受け入れられた @CPerkins の回答に関しては、指定された equals() コードが確実に機能するとは思わない. サブクラスとスーパークラスのクラスは同じではありません。

于 2013-05-10T16:46:56.803 に答える
1

親(スーパー)クラスがequalsをオーバーライドしていないようです。この場合、サブクラスでこのメソッドをオーバーライドするときに、親クラスのフィールドを比較する必要があります。コモンズ EqualsBuiler を使用するのが最善の方法であることに同意しますが、equals 契約の対称性/透過的な部分を壊さないように注意する必要があります。

サブクラスが親クラスに属性を追加し、親クラスが抽象的ではなく、equals をオーバーライドする場合、問題が発生します。このシナリオでは、継承ではなく、オブジェクトの構成に注目する必要があります。

これについては、Joshua Block による「Effective Java」のセクションを強くお勧めします。それは包括的で、本当によく説明されています。

于 2010-01-14T22:57:38.490 に答える
1

スーパークラスの equals() と hashCode() がまだ存在していれば、IDE の自動生成はおそらくスーパークラスを考慮していることに注意してください。つまり、最初にスーパーのこれら 2 つの関数を自動生成し、次に子を自動生成する必要があります。私はIntelj 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 を確認してください。

于 2016-09-08T06:32:23.717 に答える