it-swarm.com.de

Überschreiben der Java equals () -Methode - funktioniert nicht?

Ich bin heute auf ein interessantes (und sehr frustrierendes) Problem mit der equals() -Methode gestoßen, das dazu geführt hat, dass eine meiner Meinung nach gut getestete Klasse abgestürzt ist und ein Fehler aufgetreten ist, dessen Aufspüren sehr lange gedauert hat.

Der Vollständigkeit halber habe ich keinen IDE oder 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 habe einen einfachen Einkaufswagen entwickelt, der ein ArrayList von Book Objekten enthalten kann. Um die Methoden addBook(), removeBook() und hasBook() des Cart zu implementieren, wollte ich überprüfen, ob die Book bereits in der existiert Cart. Also mache ich mich auf den Weg -

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

Alles funktioniert gut beim Testen. Ich erstelle 6 Objekte und fülle sie mit Daten. Führen Sie viele Operationen zum Hinzufügen, Entfernen und Verfügen () für Cart aus, und alles funktioniert einwandfrei. Ich habe gelesen, dass Sie entweder equals(TYPE var) oder equals(Object o) { (CAST) var } haben können, aber angenommen, dass es, da es funktionierte, nicht allzu wichtig war.

Dann stieß ich auf ein Problem - ich musste ein Book -Objekt mit nur dem ID darin aus der Book-Klasse erstellen. Es werden keine weiteren Daten eingegeben. Grundsätzlich gilt:

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 funktioniert die Methode equals(Book b) nicht mehr. Es hat SEHR lange gedauert, ohne einen guten Debugger ausfindig zu machen, und vorausgesetzt, die Klasse Cart wurde ordnungsgemäß getestet und korrekt. Nachdem Sie die equals() -Methode folgendermaßen ausgetauscht haben:

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 entschieden hat, den Book-Parameter nicht zu verwenden, obwohl dies eindeutig ist? war ein Book Objekt? Der einzige Unterschied schien darin zu liegen, dass er innerhalb derselben Klasse instanziiert und nur mit einem Datenelement gefüllt wurde. Ich bin sehr sehr verwirrt. Bitte, etwas Licht ins Dunkel bringen.

148
Josh Smeaton

In Java lautet die equals() -Methode, die von Object geerbt wird:

public boolean equals(Object other);

Mit anderen Worten, der Parameter muss vom Typ Object sein.

Das ArrayList verwendet die korrekte Methode equals, bei der Sie immer die Methode aufgerufen haben, die die Gleichheit von Object nicht richtig überschrieben hat.

Ein nicht korrektes Überschreiben der Methode kann zu Problemen führen.

Ich überschreibe jedes Mal Folgendes:

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass))return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

Die Verwendung des @Override Annotation kann eine Tonne mit dummen Fehlern helfen.

Verwenden Sie es, wenn Sie glauben, die Methode einer Superklasse oder eines Interfaces zu überschreiben. Auf diese Weise erhalten Sie einen Kompilierungsfehler, wenn Sie es falsch machen.

325
jjnguy

Wenn Sie Eclipse verwenden, gehen Sie einfach zum oberen Menü

Source -> Generiere equals () und hashCode ()

108
Fred

Etwas abseits des Themas Ihrer Frage, aber es ist wahrscheinlich trotzdem erwähnenswert:

Commons Lang hat einige exzellente Methoden, die Sie zum Überschreiben von Equals und Hashcode verwenden können. Schauen Sie sich EqualsBuilder.reflectionEquals (...) und HashCodeBuilder.reflectionHashCode (...) an. Ich habe mir in der Vergangenheit viel Kopfzerbrechen erspart - obwohl es natürlich nicht Ihren Umständen entspricht, wenn Sie nur "Gleich" für den Ausweis tun möchten.

Ich stimme auch zu, dass Sie das @Override Annotation, wenn Sie Equals überschreiben (oder eine andere Methode).

11
user7094

Eine weitere schnelle Lösung, mit der Sie Code für Boilerplates speichern können, ist Lombok EqualsAndHashCode-Annotation . Es ist einfach, elegant und anpassbar. Und hängt nicht von der IDE ab . Beispielsweise;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

Unter Optionen können Sie festlegen, welche Felder in den Equals verwendet werden sollen. Lombok ist verfügbar in maven . Fügen Sie es einfach mit zur Verfügung gestellt Umfang hinzu:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>
4
borjab

Erwägen:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...
1
bcsb1001

in Android Studio ist alt + Einfügen ---> gleich und hashCode

Beispiel:

    @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}
1
David Hackro

die Anweisung instanceOf wird häufig bei der Implementierung von equals verwendet.

Dies ist eine beliebte Falle!

Das Problem ist, dass die Verwendung von instanceOf die Symmetrieregel verletzt:

(object1.equals(object2) == true)wenn und nur wenn(object2.equals(object1))

wenn das erste Gleiche wahr ist und object2 eine Instanz einer Unterklasse der Klasse ist, zu der obj1 gehört, dann gibt das zweite Gleiche false zurück!

wenn die betrachtete Klasse, zu der ob1 gehört, als final deklariert ist, kann dieses Problem nicht auftreten. Im Allgemeinen sollten Sie jedoch Folgendes testen:

this.getClass() != otherObject.getClass(); Wenn nicht, gebe false zurück, andernfalls teste die Felder, um sie auf Gleichheit zu vergleichen!

0
Nikel8000