Welche Probleme / Fallstricke müssen beim Überschreiben berücksichtigt werden equals
und hashCode
?
quelle
Welche Probleme / Fallstricke müssen beim Überschreiben berücksichtigt werden equals
und hashCode
?
equals()
( javadoc ) muss eine Äquivalenzbeziehung definieren (sie muss reflexiv , symmetrisch und transitiv sein ). Außerdem muss es konsistent sein (wenn die Objekte nicht geändert werden, muss immer derselbe Wert zurückgegeben werden). Außerdem o.equals(null)
muss immer false zurückgegeben werden.
hashCode()
( javadoc ) muss ebenfalls konsistent sein (wenn das Objekt nicht in Bezug auf geändert wird equals()
, muss es weiterhin denselben Wert zurückgeben).
Die Beziehung zwischen den beiden Methoden ist:
Wann immer
a.equals(b)
, danna.hashCode()
muss das gleiche sein wieb.hashCode()
.
Wenn Sie einen überschreiben, sollten Sie den anderen überschreiben.
Verwenden Sie denselben Feldsatz, den Sie equals()
zum Berechnen verwenden hashCode()
.
Verwenden Sie die hervorragenden Hilfsklassen EqualsBuilder und HashCodeBuilder aus der Apache Commons Lang- Bibliothek. Ein Beispiel:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
Stellen Sie bei Verwendung einer Hash-basierten Sammlung oder Map wie HashSet , LinkedHashSet , HashMap , Hashtable oder WeakHashMap sicher, dass sich der HashCode () der Schlüsselobjekte, die Sie in die Sammlung einfügen , niemals ändert, während sich das Objekt in der Sammlung befindet. Die kugelsichere Möglichkeit, dies sicherzustellen, besteht darin, Ihre Schlüssel unveränderlich zu machen, was auch andere Vorteile bietet .
instanceof
false zurückgegeben wird, wenn der erste Operand null ist (wieder effektives Java).Es gibt einige Probleme, die Sie beachten sollten, wenn Sie mit Klassen arbeiten, die mit einem Object-Relationship Mapper (ORM) wie Hibernate beibehalten werden, wenn Sie nicht der Meinung sind, dass dies bereits unangemessen kompliziert ist!
Faul geladene Objekte sind Unterklassen
Wenn Ihre Objekte mithilfe eines ORM beibehalten werden, handelt es sich in vielen Fällen um dynamische Proxys, um zu vermeiden, dass Objekte zu früh aus dem Datenspeicher geladen werden. Diese Proxys werden als Unterklassen Ihrer eigenen Klasse implementiert. Dies bedeutet, dass
this.getClass() == o.getClass()
zurückkehren wirdfalse
. Zum Beispiel:Wenn Sie mit einem ORM zu
o instanceof Person
tun haben, ist die Verwendung das einzige, was sich korrekt verhält.Faul geladene Objekte haben Nullfelder
ORMs verwenden normalerweise die Getter, um das Laden von faul geladenen Objekten zu erzwingen. Dies bedeutet , dass
person.name
sein wird ,null
wennperson
faul geladen ist, auch wennperson.getName()
Kräfte Laden und kehrt „John Doe“. Nach meiner Erfahrung tritt dies häufiger inhashCode()
und aufequals()
.Wenn Sie mit einem ORM arbeiten, stellen Sie sicher, dass Sie immer Getter verwenden und niemals Referenzen in
hashCode()
undequals()
.Durch das Speichern eines Objekts wird sein Status geändert
Persistente Objekte verwenden häufig ein
id
Feld, um den Schlüssel des Objekts zu halten. Dieses Feld wird automatisch aktualisiert, wenn ein Objekt zum ersten Mal gespeichert wird. Verwenden Sie kein ID-Feld inhashCode()
. Aber Sie können es in verwendenequals()
.Ein Muster, das ich oft benutze, ist
Aber: Sie können nicht
getId()
in aufnehmenhashCode()
. Wenn Sie dies tun, werden diehashCode
Änderungen eines Objekts geändert. Wenn sich das Objekt in a befindetHashSet
, werden Sie es "nie" wiederfinden.In meinem
Person
Beispiel würde ich wahrscheinlichgetName()
fürhashCode
undgetId()
plusgetName()
(nur für Paranoia) für verwendenequals()
. Es ist in Ordnung, wenn das Risiko von "Kollisionen" bestehthashCode()
, aber niemals in Ordnungequals()
.hashCode()
sollte die unveränderte Teilmenge der Eigenschaften von verwendenequals()
quelle
Saving an object will change it's state
!hashCode
muss zurückkehrenint
, also wie werden Sie verwendengetName()
? Können Sie ein Beispiel für IhrehashCode
Eine Klarstellung über die
obj.getClass() != getClass()
.Diese Aussage ist das Ergebnis
equals()
einer unfreundlichen Vererbung. Die JLS (Java Language Specification) gibt an, dass wennA.equals(B) == true
dannB.equals(A)
auch zurückgegeben werden musstrue
. Wenn Sie diese Anweisung weglassen, die Klassen erbt, die überschreibenequals()
(und ihr Verhalten ändern), wird diese Spezifikation verletzt.Betrachten Sie das folgende Beispiel dafür, was passiert, wenn die Anweisung weggelassen wird:
Tun Sie
new A(1).equals(new A(1))
auch,new B(1,1).equals(new B(1,1))
Ergebnis geben wahr aus, wie es sollte.Das sieht alles sehr gut aus, aber schauen Sie, was passiert, wenn wir versuchen, beide Klassen zu verwenden:
Offensichtlich ist das falsch.
Wenn Sie den symmetrischen Zustand sicherstellen möchten. a = b wenn b = a und das Liskov-Substitutionsprinzip
super.equals(other)
nicht nur im Fall einerB
Instanz aufrufen , sondern nach folgender Prüfung prüfenA
:Welches wird ausgegeben:
Wenn
a
es sich nicht um eine Referenz von handeltB
, kann es sich auch um eine Referenz der Klasse handelnA
(weil Sie sie erweitern). In diesem Fall rufen Siesuper.equals()
auch auf .quelle
ThingWithOptionSetA
gleich a sein kann,Thing
vorausgesetzt, dass alle zusätzlichen Optionen Standardwerte haben, und ebenso für aThingWithOptionSetB
, sollte es möglichThingWithOptionSetA
sein, dass aThingWithOptionSetB
nur dann mit a verglichen wird, wenn alle Nicht-Basiseigenschaften beider Objekte mit ihren Standardeinstellungen übereinstimmen , aber Ich verstehe nicht, wie Sie das testen.B b2 = new B(1,99)
, dannb.equals(a) == true
unda.equals(b2) == true
aberb.equals(b2) == false
.Informationen zu einer vererbungsfreundlichen Implementierung finden Sie in der Lösung von Tal Cohen: Wie implementiere ich die equals () -Methode korrekt?
Zusammenfassung:
In seinem Buch Effective Java Programming Language Guide (Addison-Wesley, 2001) behauptet Joshua Bloch: "Es gibt einfach keine Möglichkeit, eine instanziierbare Klasse zu erweitern und einen Aspekt hinzuzufügen, während der gleichwertige Vertrag erhalten bleibt." Tal ist anderer Meinung.
Seine Lösung besteht darin, equals () zu implementieren, indem ein anderes unsymmetrisches blindlyEquals () in beide Richtungen aufgerufen wird. blindlyEquals () wird von Unterklassen überschrieben, equals () wird geerbt und niemals überschrieben.
Beispiel:
Beachten Sie, dass equals () über Vererbungshierarchien hinweg funktionieren muss, wenn das Liskov-Substitutionsprinzip erfüllt werden soll.
quelle
if (this.getClass() != o.getClass()) return false
, aber flexibel, da es nur dann false zurückgibt, wenn sich die abgeleiteten Klassen die Mühe machen, Gleichheit zu ändern. Ist das richtig?Immer noch erstaunt, dass niemand die Guavenbibliothek dafür empfohlen hat.
quelle
this
inthis.getDate()
bedeutet nichts (außer Unordnung)if (!(otherObject instanceof DateAndPattern)) {
. Stimmen Sie Hernan und Steve Kuo zu (obwohl dies eine Frage der persönlichen Präferenz ist), aber trotzdem +1.In der Superklasse gibt es zwei Methoden: java.lang.Object. Wir müssen sie zu einem benutzerdefinierten Objekt überschreiben.
Gleiche Objekte müssen denselben Hash-Code erzeugen, solange sie gleich sind. Ungleiche Objekte müssen jedoch keine unterschiedlichen Hash-Codes erzeugen.
Wenn Sie mehr erhalten möchten, überprüfen Sie bitte diesen Link als http://www.javaranch.com/journal/2002/10/equalhash.html
Dies ist ein weiteres Beispiel, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
Habe Spaß! @. @
quelle
Es gibt verschiedene Möglichkeiten, die Klassengleichheit zu überprüfen, bevor die Mitgliedergleichheit überprüft wird, und ich denke, beide sind unter den richtigen Umständen nützlich.
instanceof
Operator.this.getClass().equals(that.getClass())
.Ich verwende # 1 in einer
final
Equals-Implementierung oder bei der Implementierung einer Schnittstelle, die einen Algorithmus für Equals vorschreibt (wie diejava.util
Sammlungsschnittstellen - der richtige Weg, um mit(obj instanceof Set)
oder der von Ihnen implementierten Schnittstelle zu prüfen ). Es ist im Allgemeinen eine schlechte Wahl, wenn Gleichheit überschrieben werden kann, da dies die Symmetrieeigenschaft bricht.Mit Option 2 kann die Klasse sicher erweitert werden, ohne dass Gleichheit überschrieben oder die Symmetrie unterbrochen wird.
Wenn Ihre Klasse auch ist
Comparable
, sollten die Methodenequals
undcompareTo
auch konsistent sein. Hier ist eine Vorlage für die equals-Methode in einerComparable
Klasse:quelle
final
dercompareTo()
Fall ist und die Methode zum Umkehren der Sortierreihenfolge überschrieben wurde, sollten Instanzen der Unterklasse und der Oberklasse nicht als gleich angesehen werden. Wenn diese Objekte zusammen in einem Baum verwendet wurden, sind Schlüssel, die gemäß einerinstanceof
Implementierung "gleich" waren, möglicherweise nicht auffindbar.Für Gleichberechtigte schauen Sie in Secrets of Equals von Angelika Langer . Ich liebe es sehr. Sie ist auch eine großartige FAQ über Generika in Java . Sehen Sie sich hier ihre anderen Artikel an (scrollen Sie nach unten zu "Core Java"), wo sie auch mit Teil 2 und "Mixed Type Comparison" fortfährt. Viel Spaß beim Lesen!
quelle
Die Methode equals () wird verwendet, um die Gleichheit zweier Objekte zu bestimmen.
da der int-Wert von 10 immer gleich 10 ist. Bei dieser Methode equals () geht es jedoch um die Gleichheit zweier Objekte. Wenn wir Objekt sagen, wird es Eigenschaften haben. Um über die Gleichheit zu entscheiden, werden diese Eigenschaften berücksichtigt. Es ist nicht erforderlich, dass alle Eigenschaften berücksichtigt werden, um die Gleichheit zu bestimmen, und in Bezug auf die Klassendefinition und den Kontext kann entschieden werden. Dann kann die equals () -Methode überschrieben werden.
Wir sollten die hashCode () -Methode immer überschreiben, wenn wir die equals () -Methode überschreiben. Wenn nicht, was wird passieren? Wenn wir in unserer Anwendung Hashtabellen verwenden, verhält sich diese nicht wie erwartet. Da der Hashcode zur Bestimmung der Gleichheit der gespeicherten Werte verwendet wird, gibt er nicht den richtigen entsprechenden Wert für einen Schlüssel zurück.
Die angegebene Standardimplementierung ist die hashCode () -Methode in der Object-Klasse. Sie verwendet die interne Adresse des Objekts und konvertiert sie in eine Ganzzahl und gibt sie zurück.
Beispiel für eine Code-Ausgabe:
quelle
Logischerweise haben wir:
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
Aber nicht umgekehrt!
quelle
Ein Fall, den ich gefunden habe, ist, dass zwei Objekte Verweise aufeinander enthalten (ein Beispiel ist eine Eltern-Kind-Beziehung mit einer praktischen Methode für den Elternteil, um alle Kinder zu erhalten).
Diese Art von Dingen ist ziemlich häufig, wenn Sie beispielsweise Zuordnungen im Ruhezustand ausführen.
Wenn Sie beide Enden der Beziehung in Ihren Hashcode aufnehmen oder Tests gleich sind, können Sie in eine rekursive Schleife geraten, die in einer StackOverflowException endet.
Die einfachste Lösung besteht darin, die getChildren-Auflistung nicht in die Methoden aufzunehmen.
quelle
equals()
. Wenn ein verrückter Wissenschaftler ein Duplikat von mir erstellen würde, wären wir gleichwertig. Aber wir hätten nicht den gleichen Vater.