Метод equals() определяет, считаются ли два объекта "равными". По умолчанию он сравнивает ссылки (т.е. ==), но часто классы (например, сущности, value-объекты) требуют логического сравнения по данным.
Когда переопределять:
Требования:
Пример:
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); } }
Если два объекта равны по equals(), их hashCode() всегда должен быть одинаковым?
Ответ: Да! Это требование контрактов Java. Но обратное неверно: два объекта с одинаковым hashCode могут быть не равны по equals() — хэш-коды могут совпасть для разных объектов (коллизии).
Пример ошибки:
person1.equals(person2); // true person1.hashCode() != person2.hashCode(); // Ошибка!
История
В корпоративном приложении сущность Пользователь попала в HashSet. Equals был переопределён, hashCode — нет. После модификации полей, влияющих на equals, HashSet "потерял" этот объект: попытка поиска методом contains возвращала false, даже для тех же данных.
История
В IoT проекте сущности временно сравнивались только по id, а затем логику сменили на equals с учётом имени. HashMap стал вести себя непредсказуемо, при обновлении ключей возникли дубликаты — нарушился контракт equals/hashCode из-за перемешивания версий реализации.
История
При написании API для сравнения заказов два разных класса реализовывали свой equals, один из них вызывал getClass(), другой — instanceof. Это привело к несимметричному сравнении заказов и багам при использовании коллекций и бизнес-логики.