Skip to content

Равенство и equals

1. Операторы сравнения

1.1 Оператор ==

  • Сравнивает ссылки на объекты
  • Проверяет, указывают ли две ссылки на один и тот же объект в памяти
  • Для примитивных типов сравнивает значения

1.2 Метод equals()

  • Сравнивает содержимое объектов
  • Наследуется от класса Object
  • По умолчанию работает так же, как ==
  • Требует переопределения для правильного сравнения объектов

2. Контракт метода equals()

2.1 Требования к реализации

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

3. Правильная реализация equals()

3.1 Базовый шаблон

@Override
public boolean equals(Object obj) {
    // 1. Проверка на ссылку на себя
    if (this == obj) return true;

    // 2. Проверка на null
    if (obj == null) return false;

    // 3. Проверка на принадлежность к тому же классу
    if (getClass() != obj.getClass()) return false;

    // 4. Приведение типов
    MyClass other = (MyClass) obj;

    // 5. Сравнение значимых полей
    return field1.equals(other.field1) &&
           field2.equals(other.field2);
}

3.2 Пример с разными типами полей

public class Student {
    private String name;
    private int id;
    private double avgGrade;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Student other = (Student) obj;

        return id == other.id && 
               Double.compare(avgGrade, other.avgGrade) == 0 &&
               Objects.equals(name, other.name);
    }
}

4. Особые случаи

4.1 Сравнение массивов

public class ArrayContainer {
    private int[] numbers;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        ArrayContainer other = (ArrayContainer) obj;
        return Arrays.equals(numbers, other.numbers);
    }
}

4.2 Сравнение с учётом наследования

public class Shape {
    private String color;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Shape)) return false;

        Shape other = (Shape) obj;
        return Objects.equals(color, other.color);
    }
}

public class Circle extends Shape {
    private double radius;

    @Override
    public boolean equals(Object obj) {
        if (!super.equals(obj)) return false;
        if (!(obj instanceof Circle)) return false;

        Circle other = (Circle) obj;
        return Double.compare(radius, other.radius) == 0;
    }
}

5. Связь с hashCode()

  • При переопределении equals() всегда нужно переопределять hashCode()
  • Если equals() возвращает true, то hashCode() должен вернуть одинаковые значения
  • Если equals() возвращает false, то hashCode() желательно должен вернуть разные значения
public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Person other = (Person) obj;
        return age == other.age && 
               Objects.equals(name, other.name);
    }

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

6. Типичные ошибки

  1. Нарушение симметричности при сравнении с подклассами
  2. Изменение сигнатуры метода (использование другого параметра вместо Object)
  3. Отсутствие проверки на null
  4. Неправильное сравнение чисел с плавающей точкой
  5. Забытое переопределение hashCode()

7. Лучшие практики

  1. Всегда использовать @Override
  2. Проверять значения на null через Objects.equals()
  3. Использовать Double.compare() для сравнения double
  4. Применять Arrays.equals() для массивов
  5. Включать все значимые поля в сравнение
  6. Соблюдать контракт equals()
  7. Всегда переопределять hashCode() вместе с equals()

8. Инструменты

  • IDE (автогенерация equals() и hashCode())
  • Apache Commons Lang (класс EqualsBuilder)
  • Lombok (@EqualsAndHashCode)
  • Objects.equals() и Objects.hash() из стандартной библиотеки

примеры

[[Programming/java/1. osnovi/Тема 5. Интерфейсы, абстрактные классы, статические методы/Урок 3. Равенство и equals/задача|задача]]