Ich bin heute auf ein interessantes (und sehr frustrierendes) Problem mit der equals()
Methode gestoßen, das dazu führte, dass eine meiner Meinung nach gut getestete Klasse abstürzte und einen Fehler verursachte, dessen Aufspüren sehr lange gedauert hat.
Der Vollständigkeit halber habe ich keine IDE oder keinen Debugger verwendet - nur einen guten, altmodischen Texteditor und System.out's. Die Zeit war sehr begrenzt und es war ein Schulprojekt.
Wie auch immer -
Ich entwickelte einen einfachen Einkaufswagen, ArrayList
der Book
Objekte enthalten konnte . Um das ,, und die Methoden des Cart zu implementieren addBook()
, wollte ich überprüfen, ob das bereits im vorhanden ist . Also los geht's -removeBook()
hasBook()
Book
Cart
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
Beim Testen funktioniert alles einwandfrei. Ich erstelle 6 Objekte und fülle sie mit Daten. Führen Sie viele Operationen zum Hinzufügen, Entfernen und Ausführen von () durch, Cart
und alles funktioniert einwandfrei. Ich habe gelesen, dass man entweder haben kann equals(TYPE var)
oderequals(Object o) { (CAST) var }
aber angenommen hat, dass es nicht allzu wichtig ist, da es funktioniert.
Dann stieß ich auf ein Problem - ich musste ein Book
Objekt mit nur dem ID
darin aus der Book-Klasse erstellen . Es würden keine anderen Daten eingegeben. Grundsätzlich gilt Folgendes:
public boolean hasBook(int i) {
Book b = new Book(i);
return hasBook(b);
}
public boolean hasBook(Book b) {
// .. more code here
return this.books.contains(b);
}
Plötzlich equals(Book b)
funktioniert die Methode nicht mehr. Das Aufspüren ohne einen guten Debugger dauerte SEHR lange und vorausgesetzt, die Cart
Klasse wurde ordnungsgemäß getestet und korrekt. Nach dem Austausch der equals()
Methode auf Folgendes:
public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
Alles begann wieder zu funktionieren. Gibt es einen Grund, warum die Methode beschlossen hat, den Book-Parameter nicht zu verwenden, obwohl es sich eindeutig um ein Book
Objekt handelt? Der einzige Unterschied schien darin zu bestehen, dass es aus derselben Klasse heraus instanziiert und nur mit einem Datenelement gefüllt wurde. Ich bin sehr sehr verwirrt. Bitte, etwas Licht ins Dunkel bringen?
quelle
Antworten:
In Java wird folgende
equals()
Methode geerbtObject
:Mit anderen Worten, der Parameter muss vom Typ sein
Object
. Dies wird als Überschreiben bezeichnet . Ihre Methodepublic boolean equals(Book other)
führt eine sogenannte Überladung derequals()
Methode durch.Das
ArrayList
verwendet überschriebeneequals()
Methoden, um Inhalte zu vergleichen (z. B. für seinecontains()
undequals()
Methoden), nicht überladene. In den meisten Ihrer CodesObject
war es in Ordnung, denjenigen aufzurufen, der die Gleichheit nicht richtig überschrieb , aber nicht kompatibel mitArrayList
.Wenn Sie die Methode nicht richtig überschreiben, kann dies zu Problemen führen.
Ich überschreibe jedes Mal Folgendes:
Die Verwendung der
@Override
Anmerkung kann einer Tonne bei dummen Fehlern helfen.Verwenden Sie es immer dann, wenn Sie glauben, die Methode einer Superklasse oder Schnittstelle zu überschreiben. Auf diese Weise erhalten Sie einen Kompilierungsfehler, wenn Sie es falsch machen.
quelle
if (!(other instanceof MyClass))return false;
Gibt zurück,false
wennMyClass
die andere Klasse erweitert wird. Aber es würde nicht zurückkehren,false
wenn die andere Klasse erweitert würdeMyClass
. Sollte nichtequal
weniger widersprüchlich sein?Wenn Sie Eclipse verwenden, gehen Sie einfach zum Hauptmenü
quelle
Etwas abseits des Themas zu Ihrer Frage, aber es ist wahrscheinlich trotzdem erwähnenswert:
Commons Lang verfügt über einige hervorragende Methoden, mit denen Sie Equals und Hashcode überschreiben können. Schauen Sie sich EqualsBuilder.reflectionEquals (...) und HashCodeBuilder.reflectionHashCode (...) an . Hat mir in der Vergangenheit viel Kopfschmerzen erspart - obwohl es natürlich nicht Ihren Umständen entspricht, wenn Sie nur "gleich" mit dem Ausweis machen möchten.
Ich bin auch damit einverstanden, dass Sie die
@Override
Annotation verwenden sollten, wenn Sie gleich (oder eine andere Methode) überschreiben.quelle
right click -> source -> generate hashCode() and equals()
,Eine weitere schnelle Lösung, die Boilerplate-Code speichert, ist die Annotation Lombok EqualsAndHashCode . Es ist einfach, elegant und anpassbar. Und hängt nicht von der IDE ab . Beispielsweise;
Sehen Sie sich die verfügbaren Optionen an, um anzupassen, welche Felder gleich verwendet werden sollen. Lombok ist in Maven verfügbar . Fügen Sie es einfach mit zur Verfügung gestellt Umfang:
quelle
in Android Studio ist alt + insert ---> gleich und hashCode
Beispiel:
quelle
Erwägen:
quelle
obj
wird als deklariertObject
. Der Punkt der Vererbung ist , dass man dann eine zuweisenBook
zuobj
. DanachObject
sollte dieser Code vollkommen legal sein und zurückkehren , es sei denn, Sie schlagen vor, dass ein nicht mit einemString
Via vergleichbar seinequals()
solltefalse
.Die
instanceOf
Aussage wird häufig bei der Implementierung von Gleichen verwendet.Dies ist eine beliebte Falle!
Das Problem ist, dass die Verwendung
instanceOf
die Symmetrieregel verletzt:(object1.equals(object2) == true)
dann und nur dann, wenn(object2.equals(object1))
Wenn die erste Gleichheit wahr ist und Objekt2 eine Instanz einer Unterklasse der Klasse ist, zu der obj1 gehört, gibt die zweite Gleichheit falsch zurück!
Wenn die betrachtete Klasse, zu der ob1 gehört, als endgültig deklariert ist, kann dieses Problem nicht auftreten. Im Allgemeinen sollten Sie jedoch Folgendes testen:
this.getClass() != otherObject.getClass();
Wenn nicht, geben Sie false zurück, andernfalls testen Sie die Felder, um sie auf Gleichheit zu vergleichen!quelle
equals()
Methode erläutert werden. Er rät von der Verwendung abgetClass()
. Der Hauptgrund ist, dass dies gegen das Liskov-Substitutionsprinzip für Unterklassen verstößt, die die Gleichheit nicht beeinträchtigen.recordId ist eine Eigenschaft des Objekts
quelle