1. Porównywanie obiektów w Javie
Obiekty w Javie można porównywać zarówno według referencji, jak i według wartości.
Porównanie linków
Jeśli dwie zmienne wskazują na ten sam obiekt w pamięci, to odwołania przechowywane w tych zmiennych są równe. Jeśli porównasz takie zmienne za pomocą operatora równości ==
, otrzymasz prawdę, co ma sens. Tutaj wszystko jest proste.
Kod | Wyjście na wyświetlaczu |
---|---|
|
|
Porównanie według wartości
Często jednak można spotkać się z sytuacją, w której dwie zmienne odnoszą się do dwóch różnych, ale identycznych obiektów. Na przykład dwa wiersze zawierające ten sam tekst, ale znajdujące się w różnych obiektach.
Aby określić tożsamość różnych obiektów, musisz użyć equals()
metody Example:
Kod | Wyjście na wyświetlaczu |
---|---|
|
|
equals
Nie tylko klasa ma metodę – wszystkie klasy w ogóleString
ją mają .
Nawet te, które tylko napiszesz, a oto dlaczego.
2. KlasaObject
Uważa się, że wszystkie klasy w Javie dziedziczą po Object
. Tak to wymyślili twórcy Javy.
A jeśli pewna klasa jest dziedziczona z klasy Object
, wszystkie metody klasy pojawiają się w tej klasie pochodnej Object
. To jest główny efekt dziedziczenia.
Innymi słowy, każda klasa, nawet jeśli nie jest zapisana w swoim kodzie, ma wszystkie metody, które ma klasa Object
.
A wśród tych metod są metody związane z porównywaniem obiektów. To jest metoda equals()
i metoda hashCode()
.
Kod | Jak to będzie w rzeczywistości: |
---|---|
|
|
W powyższym przykładzie stworzyliśmy prostą klasę Person
z parametrami nazwa i wiek, bez jednej metody. Jednak od wszystkie klasy są uważane za odziedziczone po klasie Object
, klasa Person
ma ukryte dwie metody:
metoda | Opis |
---|---|
|
Porównuje bieżący obiekt i przekazany obiekt |
|
Zwraca kod skrótu bieżącego obiektu |
Okazuje się, że equals
absolutnie wszystkie obiekty mają metody i można porównywać ze sobą obiekty różnych typów, a to wszystko się skompiluje i będzie działać idealnie.
Kod | Wyjście na wyświetlaczu |
---|---|
|
|
|
|
3. Metodaequals()
Object
Metoda odziedziczona po klasie equals()
zawiera najprostszy algorytm porównywania obiektów bieżących i przekazywanych - po prostu porównuje ich referencje.
Ten sam efekt uzyskasz, jeśli po prostu porównasz zmienne klasy Person
zamiast wywoływać metodę equals().
. Przykład:
Kod | Wyjście na wyświetlaczu |
---|---|
|
|
Metoda equals
po prostu porównuje wewnątrz łącza a
i b
.
Jednak w przypadku klasy String
porównanie działa inaczej. Dlaczego?
Ponieważ twórcy klasy String
napisali własną implementację platformy equals()
.
Implementacja metodyequals()
Napiszmy własną implementację metody equals w Person
. Spójrzmy na 4 główne przypadki.
equals
, zawsze przyjmuje parametr typu
Object
Scenariusz 1 : equals
ten sam obiekt został przekazany do metody, w której metoda została wywołana equals
. Jeśli odniesienia bieżącego i przekazanego obiektu są równe, zwróć true
. Obiekt pasuje do siebie.
W kodzie będzie to wyglądać tak:
Kod | Opis |
---|---|
|
Porównaj linki |
Scenariusz 2 : do metody equals
przekazano odwołanie null
- nie ma z czym porównywać. Obiekt, na który została wywołana metoda, equals
na pewno nie jest null, co oznacza, że w takim przypadku należy zwrócić false
.
W kodzie będzie to wyglądać tak:
Kod | Opis |
---|---|
|
Porównaj referencje Obiekt przekazany — null ? |
Scenariusz 3 : Do metody equals
zostało przekazane odwołanie do obiektu bez klasy Person
. Czy obiekt klasowy jest równy Person
obiektowi nieklasowemu Person
? Tutaj sam twórca klasy decyduje Person
- zrobi, co zechce.
Ale zwykle obiekty są nadal uważane za równe, jeśli są obiektami tej samej klasy. Dlatego jeśli do naszej metody equals został przekazany obiekt nie należący do klasy Person
, zawsze zwrócimy false
. Jak sprawdzić, jakiego typu jest obiekt? Zgadza się: używając operatora instanceof
.
Oto jak będzie wyglądał nasz nowy kod:
Kod | Opis |
---|---|
|
Porównaj referencje Obiekt przekazany — null ? Jeśli przekazany obiekt nie jest typu Person |
4. Porównanie dwóch obiektówPerson
Na czym skończyliśmy? Jeśli dotarliśmy do końca metody, to mamy obiekt typu, Person
a referencją nie jest null
. Następnie konwertujemy go na typ Person
i porównujemy wnętrza obu obiektów. To jest nasz scenariusz numer 4.
Kod | Opis |
---|---|
|
Porównaj referencje Obiekt przekazany — null ? Jeśli przekazany obiekt nie jest Person typu operacji rzutowania |
Jak porównać dwa obiekty Person
? Są równe, jeśli mają to samo imię ( name
) i wiek ( age
). Ostateczny kod będzie wyglądał następująco:
Kod | Opis |
---|---|
|
Porównaj referencje Obiekt przekazany — null ? Jeśli przekazany obiekt nie jest Person typu operacji rzutowania |
Ale to nie wszystko.
Po pierwsze pole name jest typu String
, co oznacza, że pola name muszą być porównane przy użyciu wywołania metody equals
.
this.name.equals(person.name)
Po drugie, pole name
może równie dobrze być sobie równe null
: wtedy equals
nie można z niego wywołać metody. Dodatkowa weryfikacja jest wymagana dla null
:
this.name != null && this.name.equals(person.name)
Jeśli jednak nazwa jest taka sama null
w obu obiektach Person
, nazwy są nadal równe.
Kod czwartego scenariusza może wyglądać następująco:
|
Jeśli wiek nie jest równy, natychmiast . return false Jeśli this.name równy null , nie ma sensu porównywać przez equals . Tutaj albo drugie pole name jest równe null , albo nie. Porównujemy dwa pola nazwa do equals . |
5. MetodahashCode()
Oprócz metody equals
, która dokonuje szczegółowego porównania wszystkich pól obu obiektów, istnieje jeszcze jedna metoda, która może posłużyć do niedokładnego, ale bardzo szybkiego porównania - hashCode()
.
Wyobraź sobie, że sortujesz alfabetycznie listę tysięcy słów i musisz stale porównywać słowa w parach. A słowa są długie i jest w nich dużo liter. Ogólnie rzecz biorąc, takie porównanie będzie trwało bardzo długo.
Można to jednak przyspieszyć. Załóżmy, że słowa zaczynają się na różne litery: od razu widać, że są różne. Teraz, jeśli zaczynają się od tych samych liter, nie ma gwarancji: w przyszłości słowa mogą okazać się zarówno równe, jak i różne.
Metoda hashCode()
działa w podobny sposób. Jeśli zostanie wywołany na obiekcie, zwróci określoną liczbę - odpowiednik pierwszej litery w słowie. Ta liczba ma następujące właściwości:
- Te same obiekty mają zawsze ten sam kod skrótu
- Różne obiekty mogą mieć ten sam kod skrótu lub różne
- Jeśli obiekty mają różne kody skrótu, obiekty są zdecydowanie różne
Dla lepszego zrozumienia przepisujemy te właściwości w odniesieniu do słów:
- Te same słowa mają zawsze tę samą pierwszą literę
- Różne słowa mogą mieć takie same pierwsze litery lub mogą mieć różne.
- Jeśli słowa mają różne pierwsze litery, słowa są zdecydowanie różne
Ostatnia właściwość służy do przyspieszonego porównywania obiektów:
Najpierw obliczane są kody skrótu dla dwóch obiektów. Jeśli te kody skrótu są różne, to obiekty są zdecydowanie różne i nie ma potrzeby ich dalszego porównywania.
Ale jeśli kody skrótu są takie same, nadal musisz porównywać obiekty za pomocą równości.
6. Umowy w kodzie
Zachowanie opisane powyżej musi być zaimplementowane przez wszystkie klasy w Javie. Nie ma możliwości sprawdzenia poprawności porównywania obiektów na poziomie kompilacji.
Wszyscy programiści Javy zgodzili się, że jeśli zamiast domyślnej (z klasy Object
) piszą własną implementację metody equals() , to muszą również napisać własną implementację metody hashCode()
, aby powyższe zasady zostały zachowane.
Taka umowa nazywana jest umową .
Jeśli dodasz do swojej klasy implementację tylko jednej metody equals()
lub tylko hashCode()
, rażąco naruszasz umowę (zrywasz umowę). Nie możesz tego zrobić.
Jeśli inni programiści użyją twojego kodu, może on nie działać poprawnie. Ponadto użyjesz kodu, który działa w oparciu o powyższe umowy.
We wszystkich kolekcjach w Javie, szukając elementu w kolekcji, najpierw porównuje się kod skrótu obiektów, a dopiero potem wywołuje metodę equals
.
Dlatego jeśli napiszesz swoją klasę, a w niej nową funkcję equals
, ale nie napiszesz metody hashCode()
ani nie zaimplementujesz jej z błędami, kolekcje mogą nie działać poprawnie z twoimi obiektami.
Na przykład dodałeś obiekt do listy, a następnie szukasz go za pomocą metody contains()
, ale kolekcja nie znajduje twojego obiektu.
GO TO FULL VERSION