equals()
. Реализация по умолчанию делает объект равным только самому себе (сравнение на идентичность). Это имеет смысл, если у вашего класса не бывает отдельных, но логически одинаковых экземпляров.Если два экземпляра всё-таки могут быть равны,
equals()
нужно переопределять. Реализация должна соблюдать контракт: это отношение эквивалентности (рефлексивность, транзитивность, симметричность), ни один объект не равен null
.Рефлексивность. первым делом проверим, не идентичен ли переданный объект текущему. Если да – сразу вернем
true
.Неравенство null. Если аргумент
null
– сразу вернем false
.Симметричность. Если мы допускаем наследование и расширение метода
equals()
, в наследнике может появиться дополнительная логика, которая сделает !other.equals(this)
при this.equals(other)
. Проще всего избежать этого, добавив сравнение типов. Если типы не равны – сразу вернем false
. Почему не надо использовать instanceof. Транзитивность. Оператор
==
обладает свойствами транзитивности и симметричности. Далее мы сравниваем на равенство все примитивные свойства. Для ссылочных типов этими характеристиками по контракту обладает equals
– для сравнения ссылочных типов пользуемся им.Речь здесь идет о логических свойствах. Фактически одно логическое свойство может быть представлено несколькими полями класса, или же может вычисляться на лету. Некоторые поля служат для внутренних технических нужд, и не имеют отношения к логическому состоянию. Такие поля обычно исключают из сравнения.