Czy bezpieczną praktyką jest używanie metod domyślnych jako wersji cech dla ubogich w Javie 8?
Niektórzy twierdzą, że pandy mogą być smutne, jeśli używasz ich tylko ze względu na to, że są fajne, ale nie taki jest mój zamiar. Często przypomina się również, że domyślne metody zostały wprowadzone w celu wsparcia ewolucji API i wstecznej kompatybilności, co jest prawdą, ale nie oznacza to, że używanie ich jako cech jako takich nie jest złe ani zniekształcone.
Mam na myśli następujący praktyczny przypadek użycia :
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
A może zdefiniuj PeriodTrait
:
public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}
Trzeba przyznać, że można by wykorzystać kompozycję (a nawet klasy pomocnicze), ale wydaje się ona bardziej rozwlekła i zagracona i nie pozwala skorzystać z polimorfizmu.
Czy więc można bezpiecznie / bezpiecznie używać metod domyślnych jako podstawowych cech , czy też powinienem się martwić o nieprzewidziane skutki uboczne?
Kilka pytań dotyczących SO jest związanych z cechami Java i Scala; nie o to tutaj chodzi. Nie proszę też tylko o opinie. Zamiast tego szukam autorytatywnej odpowiedzi lub przynajmniej wglądu w teren: jeśli użyłeś domyślnych metod jako cech w swoim projekcie korporacyjnym, czy okazało się, że to bomba czasowa?
źródło
Odpowiedzi:
Krótka odpowiedź brzmi: bezpiecznie się z nich korzysta :)
Wstrętna odpowiedź: powiedz mi, co masz na myśli mówiąc o cechach, a może dam ci lepszą odpowiedź :)
Mówiąc poważnie, termin „cecha” nie jest dobrze zdefiniowany. Wielu programistów Java jest najlepiej zaznajomionych z cechami wyrażanymi w Scali, ale Scala nie jest pierwszym językiem, który ma cechy, czy to w nazwie, czy w efekcie.
Na przykład w Scali cechy są stanowe (mogą mieć
var
zmienne); w Fortecy są czystym zachowaniem. Interfejsy Java z domyślnymi metodami są bezstanowe; czy to znaczy, że nie są cechami? (Podpowiedź: to było podchwytliwe pytanie.)Ponownie, w Scali cechy są komponowane poprzez linearyzację; jeśli klasa
A
rozszerza cechyX
iY
, to kolejność, w jakiejX
iY
są mieszane, określa sposób rozwiązywania konfliktów międzyX
iY
. W Javie ten mechanizm linearyzacji nie występuje (został częściowo odrzucony, ponieważ był zbyt „niepodobny do Java”).Bezpośrednim powodem dodania domyślnych metod do interfejsów była obsługa ewolucji interfejsu , ale byliśmy świadomi, że wykraczamy poza to. Niezależnie od tego, czy uważasz to za „ewolucję interfejsu ++”, czy „cechy -”, jest kwestią osobistej interpretacji. Tak więc, odpowiadając na pytanie dotyczące bezpieczeństwa ... tak długo, jak trzymasz się tego, co faktycznie obsługuje mechanizm, zamiast próbować życzliwie rozciągać go do czegoś, czego nie obsługuje, powinno być dobrze.
Kluczowym celem projektu było to, że z punktu widzenia klienta interfejsu metody domyślne powinny być nie do odróżnienia od „zwykłych” metod interfejsu. Dlatego też domyślność metody jest interesująca tylko dla projektanta i implementatora interfejsu.
Oto kilka przypadków użycia, które mieszczą się w założeniach projektowych:
Ewolucja interfejsu. Tutaj dodajemy nową metodę do istniejącego interfejsu, który ma rozsądną domyślną implementację pod względem istniejących metod w tym interfejsie. Przykładem może być dodanie
forEach
metody doCollection
, gdzie domyślna implementacja jest zapisana w odniesieniu doiterator()
metody.Metody „opcjonalne”. W tym przypadku projektant interfejsu mówi: „Implementatorzy nie muszą implementować tej metody, jeśli chcą żyć z ograniczeniami funkcjonalności, które się z tym wiążą”. Na przykład,
Iterator.remove
podano wartość domyślną, która rzucaUnsupportedOperationException
; ponieważ zdecydowana większość implementacji i takIterator
ma takie zachowanie, domyślnie ta metoda jest zasadniczo opcjonalna. (Jeśli zachowanie zAbstractCollection
zostało wyrażone jako domyślne włączoneCollection
, moglibyśmy zrobić to samo dla metod mutacyjnych).Wygodne metody. Są to metody, które są wyłącznie dla wygody, ponownie ogólnie zaimplementowane w kategoriach metod niedomyślnych w klasie.
logger()
Metoda w pierwszym przykładzie jest rozsądny tego ilustracją.Kombinatory. Są to metody kompozycyjne, które tworzą nowe instancje interfejsu w oparciu o bieżącą instancję. Na przykład metody
Predicate.and()
lubComparator.thenComparing()
są przykładami kombinatorów.Jeśli podajesz domyślną implementację, powinieneś również podać specyfikację domyślnej (w JDK używamy
@implSpec
do tego znacznika javadoc), aby pomóc implementatorom w zrozumieniu, czy chcą przesłonić metodę, czy nie. Niektóre wartości domyślne, takie jak wygodne metody i kombinatory, prawie nigdy nie są nadpisywane; inne, takie jak metody opcjonalne, są często nadpisywane. Musisz dostarczyć wystarczającą specyfikację (nie tylko dokumentację) dotyczącą tego, co domyślnie obiecuje zrobić, aby osoba wdrażająca mogła podjąć rozsądną decyzję, czy musi ją zastąpić.źródło
MouseListener
? W zakresie, w jakim ten styl interfejsu API ma sens, pasuje do segmentu „metody opcjonalne”. Upewnij się, że wyraźnie udokumentowałeś opcjonalność!MouseListener
. Dzięki za odpowiedzi.