Что обязан сделать неабстрактный класс, если его абстрактный суперкласс объявляет метод, совпадающий с default-методом интерфейса?
Неабстрактный класс обязан сам реализовать такой метод. Объявление метода в суперклассе-классе имеет приоритет над default-реализацией интерфейса, но абстрактная декларация не предоставляет тела метода, поэтому унаследованный default не может автоматически заменить её.
default-методы появились в Java 8, чтобы интерфейсы могли получать новые методы с реализацией без немедленной поломки всех существующих классов-реализаторов. При этом модель наследования классов сохранила более высокий приоритет: поведение, заданное в цепочке классов, не должно незаметно заменяться поведением интерфейса.
Это правило особенно важно для совместимости: добавление default-метода в интерфейс не должно обходить уже существующие ограничения суперкласса.
Представим, что абстрактный суперкласс объявляет метод, но оставляет его реализацию потомкам. Одновременно реализуемый интерфейс предлагает метод с той же сигнатурой и готовой default-реализацией.
Если бы Java автоматически выбрала default, класс получил бы реализацию в обход решения суперкласса. Поэтому компилятор считает метод из класса более приоритетным, но видит, что конкретной реализации всё ещё нет, и требует переопределить метод в неабстрактном классе.
Правило разрешения можно сформулировать так: метод из класса имеет приоритет над методом интерфейса, даже если метод класса абстрактный. Однако абстрактный метод нельзя вызвать как готовую реализацию, поэтому конкретный класс обязан предоставить собственное тело.
В примере Report не может унаследовать audit из Auditable: его суперкласс уже объявил одноимённый метод. Кроме того, реализация должна быть public, поскольку метод интерфейса публичный; более узкий уровень доступа недопустим.
Если Report не реализует метод, он сам должен быть объявлен abstract. Вызов через ссылку типа Auditable всё равно будет использовать реализацию Report, когда объект действительно является экземпляром конкретного подкласса.
Преимущество правила — предсказуемость и сохранение приоритета классового наследования. Компромисс состоит в том, что default-метод не всегда позволяет сделать неабстрактный класс конкретным автоматически: существующая абстрактная декларация суперкласса сохраняет обязательство реализовать метод.
В библиотеке есть абстрактный класс BaseEntity с абстрактным методом validate. Позже команда добавляет интерфейс Validatable с default-реализацией validate, рассчитывая сократить код классов-реализаторов.
Возможны два варианта. Изменить суперкласс и дать ему конкретную реализацию можно, но это затрагивает всех наследников и может изменить их поведение. Удалить абстрактный метод из суперкласса тоже рискованно: исчезнет требование, которое контролировало корректность сущностей.
Практически безопасное решение — оставить контракт суперкласса и явно реализовать validate в каждом конкретном классе, если ему подходит поведение интерфейса. Это делает выбор реализации явным, предотвращает скрытую смену семантики и сохраняет совместимость существующей иерархии.
default-реализацию?Да, если его собственное объявление не создаёт конфликт. Абстрактный класс может не реализовывать абстрактный метод интерфейса и передать эту обязанность наследникам. Но если сам класс объявил метод с совпадающей сигнатурой как abstract, конкретный подкласс уже не может просто принять default-реализацию вместо собственной.
Нет. Реализация метода интерфейса должна быть доступна как public, потому что все методы интерфейса имеют публичный контракт. Объявление метода с более узким доступом приводит к ошибке компиляции, даже если класс и интерфейс находятся в одном пакете.
default-реализацию интерфейса из переопределяющего метода?Да, если это допустимо правилами вызова для интерфейса: класс может обратиться к реализации через конструкцию вида Интерфейс.super.метод(). Но такой вызов не отменяет обязательство реализовать метод, возникшее из-за абстрактного метода суперкласса; он лишь позволяет использовать тело default внутри собственной реализации.