Ответ
Правило транзитивности, как часть общего контракта для метода equals(), формально описано в Java Language Specification (JLS) и повторено в JavaDoc класса java.lang.Object.
Контракт equals() (из JLS §4.3.2 и JavaDoc):
Для любых ненулевых ссылок x, y, z:
x.equals(x)→true(Рефлексивность)x.equals(y)→y.equals(x)(Симметричность)- Если
x.equals(y)иy.equals(z), тоx.equals(z)(Транзитивность) - Многократные вызовы
x.equals(y)возвращают одинаковый результат (Согласованность) x.equals(null)→false
Проблема транзитивности в иерархиях наследования: Нарушение часто возникает при попытке добавить в подкласс новое значимое поле для сравнения.
class Point {
private final int x, y;
// equals сравнивает только x и y
}
class ColorPoint extends Point {
private final Color color;
@Override
public boolean equals(Object o) {
if (!(o instanceof ColorPoint)) return false;
// Нарушение! ColorPoint.equals(Point) может быть false,
// но Point.equals(ColorPoint) будет true (если координаты совпадают).
return super.equals(o) && ((ColorPoint) o).color == this.color;
}
}
// Пример нарушения:
// Point p = new Point(1,2);
// ColorPoint cp1 = new ColorPoint(1,2, RED);
// ColorPoint cp2 = new ColorPoint(1,2, BLUE);
// p.equals(cp1) == true, cp1.equals(cp2) == false, но p.equals(cp2) == true. Нет транзитивности.
Рекомендация: Избегать наследования для классов, добавляющих в equals новые поля. Использовать композицию.