2013-09-02 9 views
13

Es wird in Objekt .equals(Object) javadoc erklärt:Java .equals() instanceof Unterklasse? Warum nicht Superklasse gleich nennen, anstatt sie endgültig zu machen?

Es ist symmetrisch: für jeden Nicht-Null-Referenzwerte x und y, x.equals (y) soll, wenn und nur wahr zurück, wenn y.equals (x) gibt wahr zurück.

Fast überall in Beispiel-Code sehe ich überschriebene .equals(Object) Methode, die instanceof als einer der ersten Tests verwendet, zum Beispiel hier: What issues/pitfalls must be considered when overriding equals and hashCode?

public class Person { 
    private String name; 
    private int age; 

    public boolean equals(Object obj) { 
     if (obj == null) 
      return false; 
     if (obj == this) 
      return true; 
     if (!(obj instanceof Person)) 
      return false; 
     ... 
    } 

}

Jetzt mit class SpecialPerson extends Person in equals mit:

 if (!(obj instanceof SpecialPerson)) 
      return false; 

wir garantieren nicht, dass .equals() symmetrisch ist. Es wurde zum Beispiel diskutiert hier: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals

Person a = new Person(), b = new SpecialPerson(); 

a.equals(b); //sometimes true, since b instanceof Person 
b.equals(a); //always false 

Vielleicht ist gleich zu direkten Aufruf Super I sollte am Anfang des SpecialPerson der hinzufügen?

public boolean equals(Object obj) { 
     if(!obj instanceof SpecialPerson) 
      return super.equals(obj); 
     ... 
     /* more equality tests here */ 
    } 
+0

Ähnliche Problem wird in effektiven Java diskutiert. Das kannst du lesen. – Mangoose

+2

Zu Manoose Kommentar hinzufügen - Effektive Java Sais: "Der einfachste Weg, um Probleme zu vermeiden, ist nicht zu überschreiben die Gleichheit Methode, in diesem Fall ist jede Instanz gleich nur sich selbst. Dies ist das Richtige zu tun, wenn der Folgende Bedingungen gelten: [...] Eine Oberklasse hat bereits equals überschrieben, und das von vererbte Verhalten der Oberklasse ist für diese Klasse geeignet. " – Fildor

+0

mögliches Duplikat von [Overriding equals und hashCode in Java] (http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java) –

Antwort

9

Viele der Beispiele verwenden instanceof aus zwei Gründen: a) Es faltet den Null-Check und Typ-Check in eins oder b) Das Beispiel ist für Hibernate oder ein anderes Code-Neuschreibsystem.

Die "richtige" (gemäß der JavaDoc) Lösung ist this.getClass() == obj.getClass() zu verwenden. Dies funktioniert für Java, da Klassen Singletons sind und die VM dies garantiert. Wenn Sie paranoid sind, können Sie this.getClass().equals(obj.getClass()) verwenden, aber die beiden sind wirklich gleichwertig.

Dies funktioniert die meiste Zeit. Aber manchmal müssen Java-Frameworks "schlaue" Dinge mit dem Byte-Code machen. Dies bedeutet normalerweise, dass sie automatisch einen Untertyp erstellen. Da der Subtyp dem Originaltyp gleichgesetzt werden soll, muss equals() "falsch" implementiert werden, aber das spielt keine Rolle, da die Subtypen zur Laufzeit alle bestimmten Mustern folgen. Zum Beispiel werden sie zusätzliche Dinge tun, bevor ein Setter aufgerufen wird. Dies hat keinen Einfluss auf die "Gleichheit".

Wie Sie bemerkt haben, werden die Dinge hässlich, wenn Sie beide Fälle haben: Sie erweitern wirklich die Basistypen und Sie mischen das mit automatischer Subtypgenerierung. Wenn Sie das tun, müssen Sie sicherstellen, dass Sie niemals Nicht-Blatt-Typen verwenden.

+0

Können Sie den Abschnitt der Dokumentation angeben, der Ihrem zweiten Absatz entspricht? – arshajii

+0

@arshajii: Es ist Teil des Klassenvertrags. Standard-Klassenlader geben immer den gleichen Verweis für denselben Klassennamen zurück. Wetten sind natürlich für Script-Klassenlader ausgeschaltet. –

+0

Oh, ich meinte den Teil "Die richtige Lösung ist zu benutzen ...".Willst du sagen, dass es besser ist, 'getClass()' s in 'equals()' zu vergleichen, anstatt 'instanceof' zu verwenden? – arshajii

0

Ihr Versuch, das Problem zu lösen, ist nicht korrekt. Angenommen, Sie haben 2 Unterklassen SpecialPerson und BizarrePerson. Mit dieser Implementierung können BizarrePerson Instanzen SpecialPerson Instanzen entsprechen. Das willst du generell nicht.

+2

Nun, es gibt viele Fälle, in denen es sinnvoll ist, Instanzen zu haben von zwei verschiedenen Unterklassen als gleich angesehen werden. Nehmen Sie zum Beispiel "Liste". Eine 'ArrayList' ist und sollte einer' LinkedList' entsprechen, die dieselben Elemente enthält. – arshajii

+1

Aber dann wollen Sie wahrscheinlich nicht Gleichen in Unterklassen überschreiben, worum es bei dieser Frage geht. –

1

Ihnen fehlt hier etwas. Ich werde versuchen, dies zu markieren:

Angenommen, Sie haben Person person = new Person() und Person personSpecial = new SpecialPerson() dann bin ich sicher, dass Sie nicht möchten, dass diese beiden Objekte gleich sind. Also, es funktioniert wirklich wie erforderlich, die Gleichheit muss falsch zurückgeben.

Darüber hinaus gibt die Symmetrie an, dass die Methode equals() in beiden Klassen gleichzeitig ausgeführt werden muss. Wenn einer gleich return true und anderer return false ist, würde ich sagen, dass der Fehler im Gleichheitsüberlauf liegt.

0

nicht instanceof verwenden. Verwenden Sie stattdessen this.getClass() == obj.getClass(). dann prüfen Sie genau diese Klasse.

Wenn Sie mit equals arbeiten, sollten Sie immer die hashCode verwenden und diese auch überschreiben!

die hashCode Methode zur Person könnte wie folgt aussehen:

@Override 
public int hashCode() 
{ 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + age; 
    result = prime * result + ((name == null) ? 0 : name.hashCode()); 
    return result; 
} 

und wie diese Methode in Ihrem Gleichen verwenden:

if (this.hashCode() != obj.hashCode()) 
{ 
    return false; 
} 
0

Ein Typ sollte sich auf ein Objekt von jeder nicht als gleich anderer Typ - auch ein Subtyp-- , es sei denn, beide Objekte stammen von einer gemeinsamen Klasse, deren Vertrag angibt, wie Nachkommen verschiedener Typen auf Gleichheit prüfen sollen.

Zum Beispiel könnte eine abstrakte Klasse StringyThing Zeichenketten kapseln und Methoden bereitstellen, um Dinge wie zum Beispiel in eine Zeichenkette zu konvertieren oder Teilstrings zu extrahieren, aber keine Anforderungen an das Backing-Format zu stellen. Ein möglicher Subtyp von StringyThing, zum Beispiel, könnte ein Array von StringyThing enthalten und den Wert der Verkettung all dieser Strings einkapseln. Zwei Instanzen von StringyThing würden als gleich definiert werden, wenn die Umwandlung in Strings identische Ergebnisse liefern würde, und ein Vergleich zwischen zwei ansonsten nicht unterscheidbaren Instanzen, deren Typen nichts voneinander wissen, muss möglicherweise auf diesen zurückgreifen, aber die von StringyThing abgeleiteten Typen könnten Code enthalten um verschiedene Fälle zu optimieren. Wenn zum Beispiel ein StringyThing darstellt „M Wiederholungen der Zeichen ch“ und andere darstellt „N Wiederholungen der Zeichenfolge St“, und dieser Typ weiß, um die erste, es könnte prüfen, ob St enthält nichts anderes als M/N Wiederholungen des Charakters ch. Eine solche Überprüfung würde anzeigen, ob die Strings gleich sind oder nicht, ohne eines von ihnen "ausdehnen" zu müssen.

Verwandte Themen