Интерфейсы, часть 2: методы по умолчанию.



В java 8 появилась новая возможность интерфейсов - методы с заданной реализацией:

interface Adapter {

default int get() {…}

}

Класс, который реализует интерфейс, переопределяет такой метод при необходимости.



Зачем нужны такие методы?

1️⃣ Облегчить изменения API

Во времена java 7 интерфейсы содержали только определения методов:

interface Collection<Т> {

void add(Т);

Т get();

}

Чтобы добавить, удалить или поменять сигнатуру метода нужно одновременно поменять код со стороны пользователей интерфейса и во всех реализациях. В рамках одного проекта это несложно, но для библиотек это сложная задача. Пользователи могут столкнутся с проблемами совместимости при переходе на новую версию.



Задача дефолтных методов - сгладить этот процесс и предоставить приемлемую или временную альтернативу.



2️⃣ Вспомогательные методы

Те методы, которые не входят в прямую функциональность интерфейса - методы для мониторинга, логгирования и т.д. Используйте с осторожностью, этот приём может нарушать принцип Interface segregation.



3️⃣ Комбинации базовых методов

В интерфейсе объявляется минимально необходимый набор методов, а некоторые методы являются просто их комбинациями. Мы обсуждали этот случай в прошлом посте. Такой "удобный" метод можно добавить в интерфейс как статический.



Статические методы вызываются через имя интерфейса и недоступны у экземпляров. Если это неудобно, оформите метод как метод по умолчанию.



Пример: интерфейс Comparator.

Основная функция — сравнение объектов через метод compare, индивидуальный для каждого класса. Цепочка из 2 компараторов - это 2 вызова compare и объединение результатов. Логика всегда одинакова, и нет смысла дублировать её в каждом подклассе:

default Comparator<T> thenComparing(Comparator) {…};



Что если класс реализует 2 интерфейса с методами по умолчанию?

Для разрешения конфликта используются 2 правила:

1️⃣ Если в классе переопределён метод по умолчанию — используется метод класса.

2️⃣ В иерархии интерфейсов используется метод наследника:



interface Child extends Parent

Если класс реализует интерфейс Child, то будет использоваться дефолтный метод интерфейса Child.



В опросе перед постом ни одно правило не подходит, поэтому будет ошибка компиляции. Чтобы разрешить конфликт, класс должен явно реализовать метод get();