equals()
Dzisiaj napotkałem interesujący (i bardzo frustrujący) problem z tą metodą, który spowodował awarię klasy, którą uważałem za dobrze przetestowaną, i spowodowanie błędu, którego wytropienie zajęło mi bardzo dużo czasu.
Dla kompletności nie używałem IDE ani debuggera - tylko dobry, staroświecki edytor tekstu i System.out. Czas był bardzo ograniczony i był to projekt szkolny.
W każdym razie -
Ja rozwijał podstawowy koszyk, które mogą zawierać ArrayList
od Book
obiektów . W celu wprowadzenia w życie addBook()
, removeBook()
oraz hasBook()
metod Koszyka, chciałem sprawdzić, czy Book
istniała już w Cart
. Więc idę -
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
W testowaniu wszystko działa dobrze. Tworzę 6 obiektów i wypełniam je danymi. Czy wiele dodaje, usuwa, ma operacje () na Cart
i wszystko działa dobrze. Czytałem, że możesz albo mieć, equals(TYPE var)
alboequals(Object o) { (CAST) var }
założyć, że skoro to działa, nie ma to większego znaczenia.
Potem napotkałem problem - musiałem stworzyć Book
obiekt zawierający tylko ten obiekt z ID
klasy Book. Żadne inne dane nie zostałyby do niego wprowadzone. Zasadniczo następujące:
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);
}
Nagle equals(Book b)
metoda przestaje działać. To zajęło BARDZO dużo czasu, aby wyśledzić bez dobrego debuggera i przy założeniu, że Cart
klasa została odpowiednio przetestowana i poprawna. Po przejściu equals()
metody do następującego:
public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
Wszystko znów zaczęło działać. Czy istnieje powód, dla którego metoda zdecydowała się nie przyjmować parametru Book, mimo że najwyraźniej był to Book
obiekt? Jedyna różnica polegała na tym, że został utworzony z tej samej klasy i wypełniony tylko jednym składnikiem danych. Jestem bardzo zdezorientowany. Proszę, rzuć trochę światła?
źródło
Odpowiedzi:
W Javie
equals()
dziedziczona jest metodaObject
:Innymi słowy, parametr musi być typu
Object
. Nazywa się to nadpisywaniem ; metodapublic boolean equals(Book other)
robi to, co nazywa się przeciążenia doequals()
metody.Do porównywania zawartości (np. Metod i )
ArrayList
używaequals()
metod nadpisanych , a nie przeciążonych. W większości twojego kodu wywołanie tego, który nie przesłania poprawnie równości, było w porządku, ale nie było zgodne z .contains()
equals()
Object
ArrayList
Dlatego niepoprawne zastąpienie metody może spowodować problemy.
Za każdym razem nadpisuję:
Użycie
@Override
adnotacji może pomóc tonie w głupich błędach.Użyj go, gdy myślisz, że nadpisujesz metodę superklasy lub interfejsu. W ten sposób, jeśli zrobisz to w niewłaściwy sposób, otrzymasz błąd kompilacji.
źródło
if (!(other instanceof MyClass))return false;
zwraca,false
jeśliMyClass
rozszerza inną klasę. Ale nie powróci,false
jeśli druga klasa się rozszerzyMyClass
. Nie powinnoequal
być mniej sprzeczne?Jeśli używasz zaćmienia, po prostu przejdź do górnego menu
źródło
Trochę nie na temat twojego pytania, ale i tak prawdopodobnie warto o tym wspomnieć:
Commons Lang ma kilka doskonałych metod, których możesz użyć do nadpisywania równości i hashcode. Sprawdź EqualsBuilder.reflectionEquals (...) i HashCodeBuilder.reflectionHashCode (...) . W przeszłości zaoszczędziło mi to wielu bólu głowy - chociaż oczywiście, jeśli chcesz po prostu zrobić „równa się” na legitymacji, może to nie pasować do twoich okoliczności.
Zgadzam się również, że powinieneś używać
@Override
adnotacji zawsze, gdy zastępujesz równa się (lub jakakolwiek inna metoda).źródło
right click -> source -> generate hashCode() and equals()
,Innym szybkim rozwiązaniem, które zapisuje standardowy kod, jest adnotacja Lombok EqualsAndHashCode . Jest łatwy, elegancki i konfigurowalny. I nie zależy od IDE . Na przykład;
Zobacz dostępne opcje, aby dostosować, które pola mają być używane w równych. Lombok jest dostępny w Maven . Po prostu dodaj go z podanym zakresem:
źródło
w Android Studio jest alt + insert ---> equals i hashCode
Przykład:
źródło
Rozważać:
źródło
obj
jest zadeklarowany jakoObject
. Punktem dziedziczenia jest to, że możesz następnie przypisaćBook
doobj
. Po tym, jeśli nie zasugerujesz, że aObject
nie powinno być porównywalne zString
viaequals()
, kod ten powinien być całkowicie legalny i zwracaćfalse
.instanceOf
oświadczenie jest często używany w realizacji równymi.To popularna pułapka!
Problem w tym, że używanie
instanceOf
narusza zasadę symetrii:(object1.equals(object2) == true)
wtedy i tylko wtedy gdy(object2.equals(object1))
jeśli pierwsze równa się jest prawdziwe, a obiekt2 jest instancją podklasy klasy, do której należy obiekt1, to drugie równa się zwróci fałsz!
jeśli rozpatrywana klasa, do której należy ob1, jest zadeklarowana jako ostateczna, to ten problem nie może powstać, ale generalnie należy przetestować w następujący sposób:
this.getClass() != otherObject.getClass();
jeśli nie, zwróć false, w przeciwnym razie przetestuj pola, aby porównać je pod kątem równości!źródło
equals()
metody. Odradza używaniegetClass()
. Głównym powodem jest to, że takie postępowanie łamie zasadę zastępowania Liskova dla podklas, które nie wpływają na równość.recordId jest właściwością obiektu
źródło