Jakie problemy / pułapki należy wziąć pod uwagę przy nadpisywaniu equals
i hashCode
?
źródło
Jakie problemy / pułapki należy wziąć pod uwagę przy nadpisywaniu equals
i hashCode
?
equals()
( javadoc ) musi zdefiniować relację równoważności (musi być zwrotny , symetryczny i przechodni ). Ponadto musi być spójny (jeśli obiekty nie są modyfikowane, musi zwracać tę samą wartość). Ponadto o.equals(null)
zawsze musi zwracać wartość false.
hashCode()
( javadoc ) musi być również spójny (jeśli obiekt nie został zmodyfikowany pod względem equals()
, musi zwracać tę samą wartość).
Zależność między dwóch metod:
Ilekroć
a.equals(b)
,a.hashCode()
musi być taki sam jakb.hashCode()
.
Jeśli zastąpisz jedno, powinieneś zastąpić drugie.
Użyj tego samego zestawu pól, których używasz do obliczania equals()
do obliczaniahashCode()
.
Skorzystaj z doskonałych klas pomocniczych EqualsBuilder i HashCodeBuilder z biblioteki Apache Commons Lang . Przykład:
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();
}
}
Podczas korzystania z kolekcji lub mapy opartej na haszowaniu , takich jak HashSet , LinkedHashSet , HashMap , Hashtable lub WeakHashMap , upewnij się, że hashCode () kluczowych obiektów, które umieszczasz w kolekcji, nigdy się nie zmienia, gdy obiekt jest w kolekcji. Kuloodpornym sposobem na to jest uczynienie kluczy niezmiennymi, co ma również inne zalety .
instanceof
zwraca false, jeśli jego pierwszy argument operacji ma wartość NULL (ponownie skuteczna Java).Istnieją pewne problemy, na które warto zwrócić uwagę, jeśli masz do czynienia z klasami, które są utrwalane za pomocą Mapera Relacji Obiektowych (ORM), takich jak Hibernacja, jeśli nie sądzisz, że to już było nadmiernie skomplikowane!
Leniwie ładowane obiekty są podklasami
Jeśli obiekty są utrwalane przy użyciu ORM, w wielu przypadkach będziesz mieć do czynienia z dynamicznymi serwerami proxy, aby uniknąć zbyt wczesnego ładowania obiektu z magazynu danych. Te proxy są implementowane jako podklasy własnej klasy. Oznacza to, że
this.getClass() == o.getClass()
wrócifalse
. Na przykład:Jeśli masz do czynienia z ORM, używanie
o instanceof Person
jest jedyną rzeczą, która będzie działać poprawnie.Leniwie ładowane obiekty mają pola zerowe
ORM zwykle używają modułów pobierających, aby wymusić ładowanie leniwie ładowanych obiektów. Oznacza to, że
person.name
będzie,null
jeśliperson
jest leniwie załadowany, nawet jeśliperson.getName()
wymusza ładowanie i zwraca „John Doe”. Z mojego doświadczenia wynika, że pojawia się częściej whashCode()
iequals()
.Jeśli masz do czynienia z ORM, pamiętaj, aby zawsze używać getterów i nigdy nie podawać referencji w
hashCode()
iequals()
.Zapisanie obiektu zmieni jego stan
Trwałe obiekty często używają
id
pola do przechowywania klucza obiektu. To pole zostanie automatycznie zaktualizowane przy pierwszym zapisaniu obiektu. Nie używaj pola identyfikatora whashCode()
. Ale możesz go użyć wequals()
.Często używam wzoru
Ale: nie można zawierać
getId()
whashCode()
. Jeśli to zrobisz, gdy obiekt zostanie utrwalony, jegohashCode
zmiany zostaną zmienione. Jeśli obiekt znajduje się wHashSet
„nigdy”, nie znajdziesz go ponownie.W moim
Person
przykładzie, prawdopodobnie użyłbygetName()
dohashCode
igetId()
PlusgetName()
(tylko dla paranoi) dlaequals()
. Jest w porządku, jeśli istnieje ryzyko „kolizji”hashCode()
, ale nigdy nie jest w porządkuequals()
.hashCode()
powinien używać niezmiennego podzbioru właściwości zequals()
źródło
Saving an object will change it's state
!hashCode
musi wrócićint
, więc jak będziesz używaćgetName()
? Czy możesz podać przykład swojegohashCode
Wyjaśnienie na temat
obj.getClass() != getClass()
.To stwierdzenie jest wynikiem
equals()
nieprzyjaznego dziedziczenia. JLS (specyfikacja języka Java) określa, że jeśliA.equals(B) == true
wtedyB.equals(A)
musi również zwrócićtrue
. Pominięcie instrukcji dziedziczenia klas, które zastąpiąequals()
(i zmienią jej zachowanie) spowoduje uszkodzenie tej specyfikacji.Rozważ następujący przykład tego, co dzieje się, gdy instrukcja zostanie pominięta:
Robiąc
new A(1).equals(new A(1))
również,new B(1,1).equals(new B(1,1))
wynik dawaj prawdziwy, tak jak powinien.Wszystko wygląda bardzo dobrze, ale spójrz, co się stanie, jeśli spróbujemy użyć obu klas:
Oczywiście to źle.
Jeśli chcesz zapewnić warunek symetryczny. a = b, jeśli b = a, a zasada podstawienia Liskowa wywołuje
super.equals(other)
nie tylko w przypadkuB
instancji, ale sprawdza naA
przykład po :Co da wynik:
Jeśli, jeśli
a
nie jest odwołaniem doB
, może to być odwołanie do klasyA
(ponieważ go rozszerzasz), w tym przypadkusuper.equals()
również wywołujesz .źródło
ThingWithOptionSetA
może być równeThing
pod warunkiem, że wszystkie dodatkowe opcje mają wartości domyślne, i podobnie dla aThingWithOptionSetB
, to powinno być możliweThingWithOptionSetA
porównanie a równeThingWithOptionSetB
tylko wtedy, gdy wszystkie nie-bazowe właściwości obu obiektów odpowiadają ich wartościom domyślnym, ale Nie rozumiem, jak to testujesz.B b2 = new B(1,99)
, tob.equals(a) == true
ia.equals(b2) == true
aleb.equals(b2) == false
.Aby uzyskać implementację przyjazną dziedziczeniu, sprawdź rozwiązanie Tal Cohen: Jak poprawnie wdrożyć metodę equals ()?
Podsumowanie:
W swojej książce Effective Java Programming Language Guide (Addison-Wesley, 2001) Joshua Bloch twierdzi, że „po prostu nie ma sposobu, aby rozszerzyć możliwą do utworzenia klasę i dodać aspekt przy jednoczesnym zachowaniu równości”. Tal się nie zgadza.
Jego rozwiązaniem jest zaimplementowanie equals () poprzez wywołanie innej niesymetrycznej ślepoEquals () w obie strony. blindlyEquals () jest zastępowane przez podklasy, equals () jest dziedziczone i nigdy nie jest zastępowane.
Przykład:
Zauważ, że equals () musi działać w hierarchiach dziedziczenia, jeśli zasada podstawienia Liskowa ma być spełniona.
źródło
if (this.getClass() != o.getClass()) return false
, ale elastyczne, ponieważ zwraca fałsz tylko wtedy, gdy pochodne klasy zawracają sobie głowę modyfikacją równości. Czy to prawda?Nadal jestem zdumiony, że nikt nie zalecił do tego biblioteki guava.
źródło
this
wthis.getDate()
nic nie znaczy (inne niż bałaganu)if (!(otherObject instanceof DateAndPattern)) {
. Zgadzam się z Hernanem i Steve'em Kuo (choć jest to kwestia osobistych preferencji), ale mimo to daje +1.Istnieją dwie metody w superklasie jako java.lang.Object. Musimy przesłonić je do obiektu niestandardowego.
Równe obiekty muszą generować ten sam kod skrótu, o ile są one równe, jednak nierówne obiekty nie muszą tworzyć odrębnych kodów skrótu.
Jeśli chcesz uzyskać więcej, sprawdź ten link jako http://www.javaranch.com/journal/2002/10/equalhash.html
To kolejny przykład http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
Baw się dobrze! @. @
źródło
Istnieje kilka sposobów sprawdzenia równości klas przed sprawdzeniem równości członków i myślę, że obie są przydatne w odpowiednich okolicznościach.
instanceof
operatora.this.getClass().equals(that.getClass())
.Używam nr 1 w
final
implementacji równości lub podczas implementacji interfejsu, który określa algorytm równości (np.java.util
Interfejsy kolekcji - właściwy sposób sprawdzenia za pomocą(obj instanceof Set)
lub dowolnego interfejsu, który implementujesz). Zasadniczo jest to zły wybór, gdy równość może zostać zastąpiona, ponieważ psuje to właściwość symetrii.Opcja nr 2 umożliwia bezpieczne rozszerzenie klasy bez nadpisywania równości lub łamania symetrii.
Jeśli twoja klasa jest również
Comparable
, metodyequals
i równieżcompareTo
powinny być spójne. Oto szablon dla metody równej wComparable
klasie:źródło
final
, acompareTo()
metoda została zastąpiona w celu odwrócenia kolejności sortowania, wystąpienia podklasy i nadklasy nie powinny być uważane za równe. Gdy te obiekty zostały użyte razem w drzewie, klucze, które były „równe” zgodnie zinstanceof
implementacją, mogą nie zostać znalezione.Dla równych, spójrz na Secrets of Equals autorstwa Angeliki Langer . Bardzo to kocham. Jest również świetnym FAQ na temat generics w Javie . Zobacz jej inne artykuły tutaj (przewiń w dół do „Core Java”), gdzie ona również kontynuuje część 2 i „porównanie typów mieszanych”. Miłej lektury!
źródło
Metoda equals () służy do ustalenia równości dwóch obiektów.
ponieważ wartość int 10 jest zawsze równa 10. Ale ta metoda equals () dotyczy równości dwóch obiektów. Kiedy mówimy obiekt, będzie miał właściwości. Aby podjąć decyzję o równości, bierze się pod uwagę te właściwości. Nie jest konieczne, aby wszystkie właściwości były brane pod uwagę w celu ustalenia równości, a w odniesieniu do definicji klasy i kontekstu można zdecydować. Następnie można zastąpić metodę equals ().
zawsze powinniśmy nadpisywać metodę hashCode () za każdym razem, gdy nadpisujemy metodę equals (). Jeśli nie, co się stanie? Jeśli w naszej aplikacji wykorzystamy tabele skrótów, nie będą one działały zgodnie z oczekiwaniami. Ponieważ hashCode jest używany do określania równości przechowywanych wartości, nie zwróci odpowiedniej odpowiedniej wartości dla klucza.
Domyślną implementacją jest metoda hashCode () w klasie Object używa wewnętrznego adresu obiektu i konwertuje go na liczbę całkowitą i zwraca go.
Przykładowy kod wyjściowy:
źródło
Logicznie mamy:
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
Ale nie odwrotnie!
źródło
Znalazłem jedną gotcha, w której dwa obiekty zawierają odniesienia do siebie (jednym przykładem jest relacja rodzic / dziecko z metodą wygodną dla rodzica, aby uzyskać wszystkie dzieci).
Tego rodzaju rzeczy są dość powszechne, na przykład podczas mapowania hibernacji.
Jeśli umieścisz oba końce relacji w swoim hashCode lub testy równości, możesz dostać się do pętli rekurencyjnej, która kończy się wyjątkiem StackOverflowException.
Najprostszym rozwiązaniem jest niewłączenie kolekcji getChildren do metod.
źródło
equals()
. Gdyby szalony naukowiec stworzył moją kopię, bylibyśmy równoważni. Ale nie mielibyśmy tego samego ojca.