Metoda equals() określa, czy dwa obiekty są "równe". Domyślnie porównuje referencje (tzn. ==), ale często klasy (np. encje, obiekty wartości) wymagają logicznego porównania na podstawie danych.
Kiedy nadpisywać:
Wymagania:
Przykład:
public class Person { private String email; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(email, person.email); } @Override public int hashCode() { return Objects.hash(email); } }
Jeśli dwa obiekty są równe według equals(), ich hashCode() zawsze powinien być taki sam?
Odpowiedź: Tak! To wymaganie kontraktów Java. Ale odwrotność nie jest prawdziwa: dwa obiekty o takim samym hashCode mogą nie być równe według equals() — hashe mogą się pokrywać dla różnych obiektów (kolizje).
Przykład błędu:
person1.equals(person2); // true person1.hashCode() != person2.hashCode(); // Błąd!
Historia
W aplikacji korporacyjnej encja Użytkownik trafiła do HashSet. Equals został nadpisany, hashCode — nie. Po modyfikacji pól wpływających na equals, HashSet "zgubił" ten obiekt: próba wyszukiwania metodą contains zwracała false, nawet dla tych samych danych.
Historia
W projekcie IoT encje tymczasowo porównywano tylko po id, a następnie logikę zmieniono na equals z uwzględnieniem nazwy. HashMap zaczęła zachowywać się nieprzewidywalnie, przy aktualizacji kluczy pojawiły się duplikaty — naruszono kontrakt equals/hashCode przez mieszanie wersji implementacji.
Historia
Podczas pisania API do porównywania zamówień dwie różne klasy zaimplementowały swój equals, jedna z nich wywoływała getClass(), druga — instanceof. Doprowadziło to do niesymetrycznego porównania zamówień i błędów przy użyciu kolekcji oraz logiki biznesowej.