Почему интерфейс Java не может объявить default-метод, переопределяющий equals?
Интерфейс Java не может объявить default-метод с сигнатурой equals, потому что такой метод переопределял бы метод Object. Правила языка специально запрещают default-методам переопределять методы java.lang.Object.
Интерфейс может объявить equals как абстрактный метод, но реализацию должен предоставить класс. При вызове equals у объекта приоритет всегда имеет реализация класса, унаследованная или объявленная самим классом, а не default-реализация интерфейса.
Default-методы появились в Java 8, чтобы добавлять новые методы в уже существующие интерфейсы без обязательного изменения всех классов-реализаций. Интерфейс получил возможность содержать готовую реализацию метода, что помогло развивать стандартные API с сохранением совместимости.
Методы Object имеют особый статус: каждый ссылочный объект Java уже обладает ими через корневой класс Object. Поэтому их реализацию должен определять класс объекта, а не произвольный интерфейс.
Если бы интерфейс мог задать default-реализацию equals, один и тот же класс мог бы получить конкурирующие правила равенства от суперкласса и интерфейса. Это нарушило бы обычную модель приоритетов: методы класса должны иметь преимущество перед default-методами интерфейсов.
Кроме того, equals связан с hashCode. Реализация равенства только в интерфейсе могла бы не согласовываться с хеш-кодом класса и приводить к ошибкам при использовании объектов в HashMap или HashSet.
Компилятор запрещает именно default-реализацию метода, соответствующего методу Object:
Такой интерфейс не компилируется: default-метод не может переопределять метод Object. Причина не в невозможности записать такую сигнатуру, а в специальном ограничении правил default-методов.
Абстрактное объявление допустимо:
Оно не предоставляет реализацию и лишь формулирует контракт для реализующих классов. На практике класс должен самостоятельно согласовать equals и hashCode, соблюдая их общий контракт.
Обычный default-метод, не совпадающий с методом Object, может быть унаследован классом. Если класс сам объявляет метод с такой сигнатурой, его реализация имеет приоритет. Если два интерфейса предоставляют конфликтующие default-методы, класс обязан разрешить конфликт явно, но это правило не превращает default-метод в допустимую замену equals.
Для общей логики сравнения лучше использовать отдельный метод интерфейса с другим именем, например sameIdentity, либо вынести сравнение в утилитный класс. Это не изменяет полиморфное поведение Object.equals, зато позволяет повторно использовать алгоритм без нарушения правил языка.
Предположим, несколько классов должны сравниваться по бизнес-идентификатору. Размещение default-реализации equals в интерфейсе выглядит удобным, но невозможно и потенциально опасно: классы могут иметь разные поля идентичности, а hashCode потребует согласованной реализации.
Вариант с абстрактным equals в интерфейсе формализует требование, но не предотвращает ошибочную реализацию в каждом классе. Вариант с отдельным default-методом sameIdentity технически допустим, однако он не будет автоматически использоваться коллекциями Java.
На практике обычно выбирают явную реализацию equals и hashCode в каждом конкретном классе или общий базовый класс, если у классов действительно единая модель идентичности. Это сохраняет стандартную семантику Object и делает поведение HashSet и HashMap предсказуемым.
Можно ли объявить в интерфейсе абстрактный метод equals?
Да, абстрактное объявление допустимо. Оно не переопределяет реализацию Object через default-механику и не содержит тела метода. Реализующий класс всё равно обязан предоставить совместимую реализацию, иначе он должен остаться абстрактным.
Может ли default-метод интерфейса иметь имя hashCode или toString с соответствующей сигнатурой?
Нет. Ограничение распространяется не только на equals, но и на методы Object, включая hashCode и toString. Интерфейс может объявить для них абстрактные контракты, но не default-реализации.
Что произойдёт, если класс реализует интерфейс с обычным default-методом и одновременно наследует метод с такой же сигнатурой от Object?
Реализация класса или суперкласса имеет приоритет над default-методом интерфейса. Поэтому метод Object не заменяется default-реализацией интерфейса. Это обеспечивает предсказуемое правило: конкретный метод класса важнее поведения, предоставленного интерфейсом.