Безопасно ли использовать методы по умолчанию в качестве плохой версии трейтов в Java 8?
Некоторые утверждают, что это может расстроить панд, если вы используете их только ради этого, потому что это круто, но это не мое намерение. Также часто напоминают, что методы по умолчанию были введены для поддержки эволюции API и обратной совместимости, что верно, но это не делает неправильным или искаженным использование их как характеристик как таковых.
Я имею в виду следующий практический пример использования :
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
Или, возможно, определите PeriodTrait
:
public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}
Следует отметить, что можно использовать композицию (или даже вспомогательные классы), но она кажется более многословной и загроможденной и не позволяет извлечь выгоду из полиморфизма.
Итак, можно ли / безопасно использовать методы по умолчанию в качестве основных свойств , или мне следует беспокоиться о непредвиденных побочных эффектах?
Несколько вопросов по SO связаны с особенностями Java vs Scala; дело не в этом. Я тоже не прошу просто мнений. Вместо этого я ищу авторитетный ответ или, по крайней мере, полевое понимание: если вы использовали методы по умолчанию в качестве черт в своем корпоративном проекте, оказалось ли это бомбой замедленного действия?
источник
Ответы:
Короткий ответ: безопасно, если вы используете их безопасно :)
Язвительный ответ: скажите мне, что вы имеете в виду под чертами характера, и, может быть, я дам вам лучший ответ :)
Если серьезно, то термин «черта» не имеет четкого определения. Многие Java-разработчики больше всего знакомы с чертами, выраженными в Scala, но Scala - далеко не первый язык, имеющий черты, как по названию, так и по сути.
Например, в Scala трейты сохраняют состояние (могут иметь
var
переменные); в Fortress это чистое поведение. Интерфейсы Java с методами по умолчанию не имеют состояния; Значит ли это, что они не черты характера? (Подсказка: это был вопрос с подвохом.)Опять же, в Scala трейты составляются посредством линеаризации; если класс
A
расширяет чертыX
иY
, то порядок , в которомX
иY
смешиваются в определяет , как конфликты междуX
иY
решены. В Java этого механизма линеаризации нет (он был отклонен отчасти потому, что он был слишком "не-Java-подобным").Непосредственной причиной добавления методов по умолчанию к интерфейсам была поддержка эволюции интерфейса , но мы прекрасно понимали, что идем дальше этого. Считаете ли вы это «эволюцией интерфейса ++» или «чертами--» - это вопрос личного толкования. Итак, чтобы ответить на ваш вопрос о безопасности ... до тех пор, пока вы придерживаетесь того, что механизм фактически поддерживает, вместо того, чтобы пытаться желательно растянуть его до чего-то, что он не поддерживает, все должно быть в порядке.
Ключевой целью дизайна было то, что с точки зрения клиента интерфейса методы по умолчанию должны быть неотличимы от «обычных» методов интерфейса. Таким образом, значение метода по умолчанию интересно только разработчику и разработчику интерфейса.
Вот несколько вариантов использования, которые соответствуют целям дизайна:
Эволюция интерфейса. Здесь мы добавляем новый метод к существующему интерфейсу, который имеет разумную реализацию по умолчанию с точки зрения существующих методов в этом интерфейсе. Примером может быть добавление
forEach
метода вCollection
, где реализация по умолчанию написана в терминахiterator()
метода.«Необязательные» методы. Здесь разработчик интерфейса говорит: «Разработчикам не нужно реализовывать этот метод, если они хотят жить с ограничениями функциональности, которые влекут за собой». Например,
Iterator.remove
было дано значение по умолчанию, которое выбрасываетUnsupportedOperationException
; поскольку подавляющее большинство реализаций вIterator
любом случае имеют такое поведение, значение по умолчанию делает этот метод необязательным. (Если поведение fromAbstractCollection
было выражено как значение по умолчаниюCollection
, мы могли бы сделать то же самое для изменяющих методов.)Методы удобства. Это методы, которые используются строго для удобства, опять же, как правило, реализуются в терминах методов класса, отличных от стандартных.
logger()
Метод в первом примере является разумной иллюстрацией.Комбинаторы. Это композиционные методы, которые создают новые экземпляры интерфейса на основе текущего экземпляра. Например, методы
Predicate.and()
илиComparator.thenComparing()
являются примерами комбинаторов.Если вы предоставляете реализацию по умолчанию, вы также должны предоставить некоторую спецификацию для нее (в JDK мы используем
@implSpec
для этого тег javadoc), чтобы помочь разработчикам понять, хотят они переопределить метод или нет. Некоторые значения по умолчанию, такие как удобные методы и комбинаторы, почти никогда не отменяются; другие, например необязательные методы, часто переопределяются. Вам необходимо предоставить достаточную спецификацию (а не только документацию) о том, что обещает сделать стандарт по умолчанию, чтобы разработчик мог принять разумное решение о том, нужно ли им его переопределить.источник
MouseListener
? В той степени, в которой этот стиль API имеет смысл, он укладывается в корзину «необязательных методов». Обязательно четко задокументируйте необязательность!MouseListener
. Спасибо за ответ.