Ответ 1
Короткий ответ: это безопасно, если вы используете их безопасно:)
Снежный ответ: скажите мне, что вы подразумеваете под чертами, и, может быть, я дам вам лучший ответ:)
Во всей серьезности термин "признак" не определен. Многие разработчики Java больше всего знакомы с чертами, так как они выражены в Scala, но Scala далек от первого языка, чтобы иметь черты, как по имени, так и по сути.
Например, в Scala, признаки имеют состояние (могут иметь переменные var
); в Крепости они чистое поведение. Интерфейсы Java со стандартными методами не имеют состояния; означает ли это, что они не являются чертами? (Подсказка: это был трюк.)
Опять же, в Scala, черты формируются посредством линеаризации; если класс A
расширяет черты X
и Y
, то порядок, в котором X
и Y
смешиваются, определяет, как разрешаются конфликты между X
и Y
. В Java этот механизм линеаризации отсутствует (он был частично отвергнут, потому что он был слишком "не-Java-подобным".)
Близкая причина добавления методов по умолчанию к интерфейсам заключалась в поддержке эволюции интерфейса, но мы хорошо знали, что мы выходим за рамки этого. Независимо от того, считаете ли вы, что "интерфейс эволюции ++" или "черты" - это вопрос личной интерпретации. Итак, чтобы ответить на ваш вопрос о безопасности... пока вы придерживаетесь того, что механизм действительно поддерживает, вместо того, чтобы пытаться отвлечь его на что-то, что он не поддерживает, вы должны быть в порядке.
Основная цель дизайна заключалась в том, что с точки зрения клиента интерфейса методы по умолчанию должны быть неотличимы от "обычных" методов интерфейса. Поэтому значение по умолчанию для метода интересно только разработчику и разработчику интерфейса.
Вот некоторые примеры использования, которые хорошо соответствуют целям дизайна:
-
Развитие интерфейса. Здесь мы добавляем новый метод к существующему интерфейсу, который имеет разумную реализацию по умолчанию с точки зрения существующих методов этого интерфейса. Примером может быть добавление метода
forEach
кCollection
, где реализация по умолчанию записывается в терминах методаiterator()
. -
"Необязательные" методы. Здесь разработчик интерфейса говорит: "Разработчики не должны реализовывать этот метод, если они хотят жить с ограничениями в функциональности, которые влекут за собой". Например,
Iterator.remove
получил значение по умолчанию, которое выдаетUnsupportedOperationException
; так как подавляющее большинство реализацийIterator
имеют такое поведение в любом случае, значение по умолчанию делает этот метод по существу необязательным. (Если поведение изAbstractCollection
было выражено как значения по умолчанию наCollection
, мы могли бы сделать то же самое для мутативных методов.) -
Удобные методы. Это методы, которые строго для удобства, опять же обычно реализуются с точки зрения нестандартных методов для класса. Метод
logger()
в вашем первом примере является разумной иллюстрацией этого. -
Комбинаторы. Это композиционные методы, которые создают новые экземпляры интерфейса на основе текущего экземпляра. Например, методы
Predicate.and()
илиComparator.thenComparing()
являются примерами комбинаторов.
Если вы предоставили реализацию по умолчанию, вы также должны указать некоторую спецификацию по умолчанию (в JDK мы используем тег @implSpec
javadoc для этого), чтобы помочь разработчикам понять, хотят ли они переопределить метод или нет. Некоторые значения по умолчанию, такие как методы удобства и комбинаторы, почти никогда не переопределяются; другие, как необязательные методы, часто переопределяются. Вам нужно предоставить достаточную спецификацию (а не только документацию) о том, что делать по умолчанию promises, поэтому разработчик может принять разумное решение о необходимости ее переопределения.