Зачем в Java переопределять метод equals()?

Ответ

Метод equals() переопределяют, чтобы сравнивать объекты по их внутреннему состоянию (значениям полей), а не по ссылке, как это делает реализация по умолчанию из класса Object.

Пример:

public class Person {
    private String name;
    private 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);
    }
}

Почему это важно:

  • Коллекции: Без корректного equals() коллекции вроде ArrayList.contains() или HashSet будут работать неправильно.
  • Контракт с hashCode(): При переопределении equals() обязательно нужно переопределить hashCode(), чтобы объекты, которые равны по equals(), имели одинаковый хэш-код. Это требование для корректной работы HashMap и HashSet.
  • Принципы: Реализация должна быть рефлексивной, симметричной, транзитивной и консистентной. Используйте Objects.equals() для безопасного сравнения полей, которые могут быть null.

Ответ 18+ 🔞

А, ну вот, опять про этот ваш equals(), классика жанра, блядь! Сидит такой джавист, думает: «О, у меня два человека, оба Васей зовут, оба по тридцать лет — ну это же один и тот же мужик, ёпта!». А Java ему такая: «Хуй там, сынок, это две разные ссылки в памяти, нихуя не равны». И пошёл наш страдалец equals() переопределять, чтобы логику человеческую, а не машинную, впилить.

Смотри, вот тебе живой пример, чтоб не быть мудаком:

public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // если это один и тот же объект — сразу true, нехуй мозги парить
        if (o == null || getClass() != o.getClass()) return false; // если null или вообще не Person — false, иди нахуй
        Person person = (Person) o; // приводим тип, теперь можно поля сравнивать
        return age == person.age && Objects.equals(name, person.name); // вот она, магия, сравниваем по смыслу!
    }
}

А нахуя это всё, спросишь? Да затем, что без этого — пиздец, а не жизнь!

  • Коллекции, сука! Возьмёшь HashSet, положишь туда Васю. Потом создашь другого Васю, с теми же полями, и спросишь: «А есть у меня тут Вася?». А HashSet тебе, с видом идиота: «Нету, блядь, я по ссылкам смотрю, а это новая ссылка». И сидишь ты, обосранный, с пустым результатом. А с правильным equals() — всё работает, как часы, находит твоего Васю по содержимому.
  • Контракт с hashCode() — это святое, ёбана! Это как близнецы-братья: переопределил equals()обязательно переопределяй и hashCode(). Иначе HashMap и HashSet просто сойдут с ума и начнут терять твои объекты в своих недрах. Правило железное: если два объекта равны по equals(), их хэш-коды должны совпадать. Иначе — welcome to адская отладка.
  • Принципы, блядь, не выдумывай! Реализация должна быть предсказуемой: рефлексивной (объект равен сам себе), симметричной (если А равен Б, то и Б равен А), транзитивной и консистентной (сколько ни сравнивай — результат один). И главный лайфхак — используй Objects.equals() для сравнения строк и прочего, что может быть null, чтобы не получить NullPointerException в самый неподходящий момент, как оплеуху.

Вот и вся магия, не такой уж он и страшный, этот equals(). Главное — делай с умом и не забудь про его кореша hashCode(), а то будет тебе, как Герасиму с Муму — вроде и хотел как лучше, а получил сплошную трагедию и совесть, которая жрёт изнутри.