Что такое транзитивность в контексте контракта equals в Java?

«Что такое транзитивность в контексте контракта equals в Java?» — вопрос из категории ООП, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Транзитивность — одно из ключевых требований контракта метода equals(Object) в Java. Оно гласит: если a.equals(b) возвращает true и b.equals(c) возвращает true, то a.equals(c) также должно возвращать true.

Для чего это нужно:

  • Корректная работа коллекций: Нарушение транзитивности ломает логику HashSet, HashMap и других структур, основанных на хэшах и сравнении.
  • Логическая целостность: Отношение равенства должно быть последовательным и предсказуемым.

Пример нарушения (ошибка в реализации equals):

class BadPoint {
    private int x;
    private int y;
    // Конструктор, геттеры...

    @Override
    public boolean equals(Object o) {
        // ОШИБКА: Сравниваем только координату X, игнорируя Y
        if (!(o instanceof BadPoint)) return false;
        BadPoint p = (BadPoint) o;
        return this.x == p.x;
    }
}

Что пойдет не так:

BadPoint p1 = new BadPoint(1, 10); // (x=1, y=10)
BadPoint p2 = new BadPoint(1, 20); // (x=1, y=20)
BadPoint p3 = new BadPoint(2, 20); // (x=2, y=20)

System.out.println(p1.equals(p2)); // true (x совпадает)
System.out.println(p2.equals(p3)); // false (x разный)
System.out.println(p1.equals(p3)); // false (x разный) -> НАРУШЕНИЕ ТРАНЗИТИВНОСТИ!
// Поскольку p1 == p2 и p2 != p3, мы ожидаем, что p1 != p3. Но формально условие нарушено.
// В HashSet могут оказаться «равные» объекты p1 и p2 одновременно.

Правильная реализация должна сравнивать все значимые поляx, и y в данном случае).