Как equals сравнивает объекты

Ответ

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

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

@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);
}

Важные правила:

  • Рефлексивность: x.equals(x)true
  • Симметричность: x.equals(y) == y.equals(x)
  • Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
  • Консистентность: повторные вызовы дают одинаковый результат
  • x.equals(null)false

Ответ 18+ 🔞

А, ну это же классика, ёпта! Сейчас объясню на пальцах, чтобы даже мартышлюшка поняла.

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

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

Вот смотри, как это обычно выглядит в коде. Представь, у нас класс Person (Человек, не полупидор какой-нибудь).

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

Видишь? Всё логично. Сначала отсекаем очевидную хуйню, потом кастуем и сравниваем каждое поле. Objects.equals() — это такая удобная штука, которая не сломается, если name будет null.

А теперь, чувак, самое важное — правила, которые нельзя нарушать, иначе весь твой код превратится в пиздопроебибну, которую невозможно отладить. Их как заповеди помни:

  • Рефлексивность: x.equals(x) всегда должен возвращать true. Объект должен быть равен самому себе, это же ебать очевидно! Если нет — ты конченый.
  • Симметричность: Если x.equals(y) вернул true, то и y.equals(x) обязан вернуть true. Не может быть так, что Вася равен Пете, а Петя Васе — нет. Это уже какая-то хитрая жопа получается.
  • Транзитивность: Если x равен y, а y равен z, то x обязан быть равен z. Логика, блядь, а не хуй с горы.
  • Консистентность: Сколько раз ни вызывай equals() на неизменённых объектах — результат должен быть одинаковый. Не может он сегодня сказать true, а завтра — false. Иначе это волнение ебать на ровном месте.
  • Сравнение с null: x.equals(null) всегда должен быть false. Никаких NullPointerException! Просто false и точка.

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

Короче, переопределяй equals() с умом, и будет тебе счастье. А не переопределишь или сделаешь криво — сам потом будешь ебать копать, ища баг.