Зачем нужен метод equals() из класса Object

Ответ

Метод equals() используется для сравнения объектов на содержательное равенство, а не просто ссылочное (как ==). По умолчанию он сравнивает ссылки, но его можно переопределить для логики сравнения полей.

Пример переопределения:

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Person person = (Person) obj;
    return age == person.age && Objects.equals(name, person.name);
}

Важно:

  • Всегда переопределять hashCode() вместе с equals() (иначе нарушится контракт для коллекций).
  • Проверять тип (instanceof или getClass()).
  • Учитывать null и рефлексивность/симметричность/транзитивность.

Ответ 18+ 🔞

Э, слушай, вот тебе история про этот ваш equals(). Ну, то есть, не моя история, а вообще, как оно работает. Представь, что ты в баре, и тебе показывают две одинаковые бутылки пива. По этикетке — одно и то же, но одна в твоей руке, а другая у соседа на столе. Оператор == — это как посмотреть на руку: «Это моя бутылка? Нет, это его бутылка. Значит, не одно и то же». А equals() — это как попробовать: «О, оба — «Балтика №9»! По содержанию — одинаковое говно! Значит, равны».

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

Вот смотри, как это выглядит в коде, если бы мы сравнивали людей:

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

Вот и вся магия. Но есть, сука, важные нюансы, про которые все постоянно забывают, а потом у них всё ебётся.

Главные правила, которые надо выжечь себе на подкорке:

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

  2. Тип проверяй. Обязательно убедись, что тебе скормили объект того же класса. instanceof или getClass() — выбирай по ситуации, но не пропускай этот шаг. А то какой-нибудь пидарас шерстяной в виде объекта Car пролезет в сравнение с Person, и всё упадёт с ClassCastException.

  3. Не забудь про null. Сравнивать с null всегда должно возвращать false. Это как спросить у пустого стула: «Ты Вася?». Нет, стул — не Вася.

  4. Свойства контракта. Рефлексивность (x.equals(x) — всегда true), симметричность (если x.equals(y), то и y.equals(x)) и транзитивность. Звучит занудно, но если на этом сэкономить, получится такая пиздопроебибна в логике, что объекты будут равны сами себе только по вторникам.

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