Как правильно реализовать метод equals() в Java?

Ответ

При реализации equals() необходимо соблюдать его контракт:

  1. Рефлексивность: x.equals(x)true.
  2. Симметричность: x.equals(y)true тогда и только тогда, когда y.equals(x)true.
  3. Транзитивность: Если x.equals(y) и y.equals(z), то x.equals(z).
  4. Согласованность: Многократные вызовы возвращают одинаковый результат, если объекты не изменялись.
  5. Сравнение с null: x.equals(null)false.

Обязательно переопределяйте hashCode(), чтобы объекты, признанные равными по equals(), имели одинаковый хеш-код. Это критично для корректной работы HashMap, HashSet.

Шаблон реализации с использованием java.util.Objects:

@Override
public boolean equals(Object o) {
    // 1. Проверка ссылки
    if (this == o) return true;
    // 2. Проверка типа и null
    if (o == null || getClass() != o.getClass()) return false;
    // 3. Приведение типа и сравнение полей
    MyClass other = (MyClass) o;
    return Objects.equals(this.field1, other.field1) &&
           Objects.equals(this.field2, other.field2) &&
           this.field3 == other.field3; // для примитивов
}

@Override
public int hashCode() {
    // Генерация хеш-кода на основе тех же полей, что и в equals()
    return Objects.hash(field1, field2, field3);
}

Важные моменты:

  • Используйте Objects.equals() для безопасного сравнения ссылок (включая null).
  • Для примитивных типов используйте ==.
  • Сначала сравнивайте наиболее «дешевые» или чаще различающиеся поля.
  • Рассмотрите возможность использования автоматической генерации через IDE или Lombok (@EqualsAndHashCode).

Ответ 18+ 🔞

Давай разберём эту тему, а то некоторые пишут equals() так, что потом сами от себя охуевают, когда HashMap начинает вести себя как пизда с ушами.

Слушай, контракт equals() — это не просто бумажка, это, блядь, священный договор с виртуальной машиной. Нарушишь — получишь неопределённое поведение, и будешь потом неделю дебажить, почему у тебя в HashSet дубликаты плодятся.

Вот эти пять пунктов — это как пять заповедей, ёпта. Особенно про симметричность. Классика жанра: пишешь ты класс Employee, наследуешься от Person, переопределяешь equals() только в Employee. Потом сравниваешь employee.equals(person)false, а person.equals(employee)true. И всё, пиздец, симметричность нарушена, коллекции ебут мозг.

А теперь главное, на чём все обламываются: hashCode() и equals() — это неразлучная парочка, как хуй с яйцами. Если переопределил один — обязан переопределить и второй, и так, чтобы для равных объектов хеш-коды были одинаковые. Иначе твой объект в HashMap просто потеряется, как хуй в стоге сена. Объекты равны, а хеши разные — ищи-свищи свою запись, её там нет.

Шаблон-то, в принципе, норм, но давай я его на пальцах объясню:

@Override
public boolean equals(Object o) {
    // 1. Это же я? Если да — сразу true, нехуй мозги парить.
    if (this == o) return true;
    // 2. Это null или объект другого класса? Если да — false, и не спорь.
    if (o == null || getClass() != o.getClass()) return false;
    // 3. Теперь кастуем и сравниваем поля.
    MyClass other = (MyClass) o;
    return Objects.equals(this.name, other.name) &&
           this.age == other.age; // примитивы через ==
}

Objects.equals() — это гениальная штука, она сама проверяет на null. Раньше-то писали (field == null ? other.field == null : field.equals(other.field)). Сейчас можно не выёбываться.

И ради всего святого, сравнивай сначала те поля, которые с наибольшей вероятностью различаются. Не надо сначала тяжеловесные объекты или строки в километр тащить. Сравнил id — и сразу false, если не совпали. Экономия процессорного времени, ёбана.

Про hashCode(): Objects.hash() — твой друг. Туда пихаешь те же поля, что и в equals(). Всё, больше ни о чём не думаешь.

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

И последнее: если лень это всё писать — поставь Lombok и навесь @EqualsAndHashCode над классом. Он сделает всё за тебя, правильно и без косяков. Но хотя бы раз в жизни напиши это руками, чтобы понимать, как эта магия работает изнутри, а не быть, прости господи, мартышлюшкой, которая тыкает в кнопки.

Запомни: правильно написанные equals() и hashCode() — это фундамент, на котором не проседает вся остальная архитектура. На этом не экономь, а то потом волнение ебать будет, когда продакшн упадёт в три часа ночи.