webentwicklung-frage-antwort-db.com.de

Überschreiben von equals () & hashCode () in Unterklassen ... unter Berücksichtigung von Superfeldern

Gibt es eine bestimmte Regel, wie equals() & hashCode() in sub-Klassen überschrieben wird, wenn Superfelder berücksichtigt werden? wissend, dass es viele Parameter gibt: Super Felder sind privat/öffentlich, mit/ohne Getter ...

Zum Beispiel werden von netbeans equals () & hashCode () die Super-Felder nicht berücksichtigt ... und

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

wird wahr zurückkehren :(

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

Wenn Sie sehen möchten, dass Netbeans equals () & hashCode () generiert:

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;
    }
}
55
wj.

Kinder sollten nicht die privaten Mitglieder ihrer Eltern untersuchen

Aber offensichtlich , sollten alle wichtigen Bereiche für Gleichheit und Hashing berücksichtigt werden.

Glücklicherweise können Sie beide Regeln problemlos erfüllen.

Vorausgesetzt, Sie sind nicht mit dem von NetBeans generierten Equals und Hashcode festgefahren, können Sie die Gleichheitsmethode von Hominidae so ändern, dass Instanzvergleich und nicht Klassengleichheit verwendet wird. Etwas wie das:


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

Natürlich ist Hashcode einfach:


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

Aber im Ernst: Was ist los mit NetBeans, die Superklassenfelder nicht berücksichtigen, indem sie die Superklassenmethoden aufrufen?

56
CPerkins

Ich bevorzuge die Verwendung von EqualsBuilder (und HashcodeBuilder) aus dem commons-lang-Paket , um meine equals () - und hashcode () - Methoden deutlich lesbarer zu machen.

Beispiel:

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

Generell ist die Implementierung von Equals über Unterklassen hinweg schwer symmetrisch und transitiv zu halten.

Stellen Sie sich eine Superklasse vor, die auf das Feld x und y und die Unterklassenprüfung auf x, y und z prüft.

Eine Unterklasse == Oberklasse == Unterklasse, bei der sich z zwischen der ersten Instanz der Unterklasse und der zweiten unterscheidet, was den transitiven Teil des Vertrages verletzt.

Dies ist der Grund, warum die typische Implementierung von equals auf getClass() != obj.getClass() prüft, anstatt eine Instanz davon auszuführen. Wenn im obigen Beispiel SubClass oder Superclass eine Überprüfung der Instanz durchführt, wird die Symmetrie gebrochen.

Das Ergebnis ist, dass eine Unterklasse sicherlich super.equals () berücksichtigen kann, aber sie sollte auch eine eigene getClass () - Prüfung durchführen, um die oben genannten Probleme zu vermeiden, und dann zusätzlich Gleichheitsmerkmale in ihren eigenen Feldern prüfen. Es wäre eine seltsame Ente einer Klasse, die ihr gleichwertiges Verhalten basierend auf bestimmten Feldern der Oberklasse geändert hat und nicht nur, wenn die Oberklasse gleich ergibt.

8
Yishai

Die Regeln sind:

  • Es ist reflexiv: Für jeden Referenzwert x, der nicht Null ist, sollte x.equals (x) den Wert true zurückgeben.
  • Es ist symmetrisch: Für alle Nicht-Null-Referenzwerte x und y sollte x.equals (y) genau dann true zurückgeben, wenn y.equals (x) true zurückgibt.
  • Es ist transitiv: Wenn für alle Nicht-Null-Referenzwerte x, y und z x.equals (y) true und y.equals (z) true zurückgibt, sollte x.equals (z) true zurückgeben.
  • Es ist konsistent: Für alle Nicht-Null-Referenzwerte x und y geben mehrere Aufrufe von x.equals (y) konsistent true zurück oder geben konsistent false zurück, vorausgesetzt, es werden keine Informationen geändert, die in Gleichheitsvergleichen der Objekte verwendet werden.
  • Für jeden Referenzwert x, der nicht Null ist, sollte x.equals (null) den Wert false zurückgeben.
  • Ist im Allgemeinen erforderlich, um die Methode hashCode zu überschreiben, wenn diese Methode überschrieben wird, um den allgemeinen Vertrag für die Methode hashCode aufrechtzuerhalten, die besagt, dass gleiche Objekte gleiche Hashcodes haben müssen

aus Object.equals ()

Verwenden Sie also die Felder, die zum Erfüllen der Regeln erforderlich sind.

5
rodrigoap

Da die Vererbung die Kapselung bricht, müssen Unterklassen, die equals () und hashCode () implementieren, notwendigerweise die Besonderheiten ihrer Oberklassen berücksichtigen. Ich hatte erfolgreiche Codierungsaufrufe an die Methoden equals () und hashCode () der übergeordneten Klasse von den Methoden der Unterklasse.

2
Steve Emmerson

In Bezug auf die akzeptierte @CPerkins-Antwort glaube ich nicht, dass der angegebene equals () - Code zuverlässig funktioniert, da die super.equals () -Methode wahrscheinlich auch auf Klassengleichheit überprüft. Eine Unterklasse und Oberklasse haben keine gleichwertigen Klassen.

2
MattRing

Nun, HomoSapiens#hashcode wird mit der Antwort von CPerkins ausreichen.

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

Wenn Sie die Felder dieser übergeordneten Objekte (gender, weight, height) verwenden möchten, können Sie eine Instanz des übergeordneten Typs erstellen und verwenden. Gott sei Dank, es ist keine abstrakte Klasse.

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

Ich füge einen Weg hinzu, um dieses Problem zu lösen. Der Schlüsselpunkt ist das Hinzufügen einer Methode, bei der die Gleichheit lose geprüft wird.

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

Und hier kommt die 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;
}

Testen...

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

Es klingt so, als ob Ihre Eltern- (Super-) Klasse Gleichgestellte nicht überschreibt. Wenn dies der Fall ist, müssen Sie die Felder der übergeordneten Klasse vergleichen, wenn Sie diese Methode in der Unterklasse überschreiben. Ich bin damit einverstanden, dass EqualsBuiler der Commons ist der Weg zu gehen, aber Sie müssen darauf achten, dass Sie die Symmetrie-/Transativteile des Equals-Kontrakts nicht brechen.

Wenn Ihre Unterklasse der übergeordneten Klasse Attribute hinzufügt und die übergeordnete Klasse nicht abstrakt ist und Gleichheitsmerkmale überschreibt, werden Sie in Schwierigkeiten geraten. In diesem Szenario sollten Sie sich die Objektzusammensetzung anstelle der Vererbung ansehen.

Ich würde den Abschnitt in Effective Java von Joshua Block zu diesem Thema wärmstens empfehlen. Es ist umfassend und sehr gut erklärt.

1
BigMikeW

Ich glaube, sie haben jetzt eine Methode, die genau das für Sie tut:

EqualsBuilder.reflectionEquals (this, o);

HashCodeBuilder.reflectionHashCode (this);

0
Terry H

Es ist erwähnenswert, dass die automatische Generierung von IDE möglicherweise die Superklasse in Betracht gezogen hat - nur vorausgesetzt, dass equals () und hashCode () der Superklasse noch existieren. Das heißt, sollten diese beiden Funktionen automatisch zuerst generieren und dann automatisch das Kind erzeugen. Ich habe unter Intellj Idea das richtige Beispiel erhalten:

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

Das Problem tritt nur dann auf, wenn Sie nicht zuerst automatisch Super's generieren. Bitte überprüfen Sie oben unter Netbeans.

0
Ming