Что мешает интерфейсу Java объявить default-реализацию equals?
Интерфейс Java не может объявить default-метод, совпадающий по сигнатуре с публичным методом Object, включая equals, hashCode и toString. Такой код запрещён на этапе компиляции: методы Object имеют особый приоритет, а их реализация должна определяться классом, а не default-методом интерфейса.
Интерфейс при этом может объявить equals как абстрактный метод. Реализация, унаследованная классом от Object, может удовлетворить этому контракту, если класс не переопределяет метод самостоятельно.
Default-методы появились в Java 8, чтобы интерфейсы можно было расширять готовой реализацией, не заставляя немедленно изменять все классы-реализации. Это решало проблему эволюции широко используемых интерфейсов.
Однако методы Object обладают базовой семантикой для каждого экземпляра класса. Разрешение интерфейсам подменять эту семантику default-реализациями создало бы неоднозначность: поведение equals, hashCode и toString зависело бы от набора реализованных интерфейсов, а не прежде всего от самого класса.
Представим интерфейс, который пытается задать общую реализацию equals для всех реализующих классов. У разных классов могут быть разные правила идентичности: сравнение по идентификатору, по всем полям или только по ссылке.
Если бы default-метод интерфейса мог автоматически заменять Object.equals, возникли бы риски нарушения контракта equals и согласованности с hashCode. Особенно опасна ситуация, когда класс уже наследует одну семантику от базового класса, а интерфейс предлагает другую.
Специальное правило Java запрещает default-метод интерфейса, который является override-equivalent публичному методу Object. Поэтому нельзя объявить default-реализацию для equals(Object), hashCode() или toString().
Абстрактное объявление equals в интерфейсе не создаёт новой реализации. Конкретный класс может удовлетворить ему унаследованным методом Object.equals либо собственным переопределением.
Обычные default-методы интерфейса могут обращаться к equals, hashCode и toString. Вызов будет полиморфным: если класс переопределил соответствующий метод, будет выбрана реализация класса. Запрет касается именно объявления default-реализации этих методов в интерфейсе.
Практическое следствие: если интерфейс описывает значение, он может задокументировать требования к равенству, но не может безопасно навязать их через default-метод. Ответственность за согласованное переопределение equals и hashCode остаётся у класса либо у общей базовой реализации.
Библиотека описывает интерфейс ValueLike для объектов, которые должны сравниваться по содержимому. Разработчик хочет поместить в интерфейс default-реализации equals и hashCode, чтобы не дублировать код.
Вариант с default-методами невозможен: компилятор отвергнет такие объявления. Вариант с абстрактным equals формально допустим, но не гарантирует корректную реализацию: класс может унаследовать ссылочное сравнение от Object, что нарушит ожидаемую семантику библиотеки.
Если иерархия закрытая и единая, разумно использовать общую базовую value-классу, которая реализует оба метода согласованно. Если реализации независимы, лучше задокументировать контракт интерфейса и требовать переопределения в каждом классе; для внешнего сравнения можно предоставить отдельный компаратор. Такой подход явно отделяет контракт интерфейса от механизма наследования методов и предотвращает скрытую несовместимость с базовым классом.
Запрещены ли default-реализации только для equals?
Нет. То же ограничение распространяется на другие публичные методы Object, прежде всего на hashCode() и toString(). Нельзя объявить в интерфейсе default-метод с такой сигнатурой, даже если реализация кажется универсальной.
Может ли класс получить реализацию equals от Object, если интерфейс объявляет equals абстрактным?
Да. Абстрактное объявление в интерфейсе задаёт требование к доступному методу, но не обязательно требует текстового объявления в классе. Публичный Object.equals может удовлетворить этому требованию; однако это не означает, что класс автоматически получил содержательно корректное сравнение по значению.
Может ли default-метод интерфейса вызвать equals и получить переопределённую реализацию класса?
Да. Запрет касается объявления default-метода с сигнатурой equals, а не вызова этого метода. Если default-метод вызывает equals у текущего объекта, обычная динамическая диспетчеризация направит вызов к переопределению в классе, если оно существует; иначе будет использована реализация Object.