Метод equals() определяет логику сравнения объектов на смысловое равенство (value equality), а не просто ссылочное (reference equality). Вот основные случаи, когда переопределение необходимо:
class Person {
String name;
int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
Без переопределения person1.equals(person2) сравнивало бы ссылки, а не данные.
Коллекции используют equals() для:
contains() проверкиremove() элементовHashSet и HashMap (в сочетании с hashCode())Set<Person> people = new HashSet<>();
people.add(new Person("Alice", 30));
people.contains(new Person("Alice", 30)); // true только с переопределенным equals()
Assertions в тестах используют equals() для сравнения ожидаемого и фактического результатов.
Для классов, где каждый экземпляр уникален по определению (например, Thread).
Если класс используется только в одном месте и сравнение по ссылкам достаточно.
Например, если наследуетесь от класса, где equals() уже корректно реализован.
Они автоматически получают корректную реализацию:
data class Person(val name: String, val age: Int) // equals() уже реализован
При переопределении важно соблюдать:
x.equals(x) → truex.equals(y) ⇔ y.equals(x)x.equals(y) и y.equals(z), то x.equals(z)x.equals(null) → falseВажное правило: если переопределяете equals(), всегда переопределяйте hashCode(), чтобы соблюдать контракт:
a.equals(b) == true, то a.hashCode() == b.hashCode()Пример нарушения:
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);
p1.equals(p2); // true
// Но если hashCode() не переопределен:
p1.hashCode() != p2.hashCode(); // Нарушение контракта
переопределяйте equals() когда нужно сравнение по содержимому, особенно для работы с коллекциями. Не делайте этого, если достаточно ссылочного сравнения или когда класс уже предоставляет подходящую реализацию. Всегда соблюдайте контракт между equals() и hashCode().