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 cechy X
i Y
, to kolejność, w jakiej X
i Y
są mieszane, określa sposób rozwiązywania konfliktów między X
i Y
. 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 do Collection
, gdzie domyślna implementacja jest zapisana w odniesieniu do iterator()
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 rzuca UnsupportedOperationException
; ponieważ zdecydowana większość implementacji i tak Iterator
ma takie zachowanie, domyślnie ta metoda jest zasadniczo opcjonalna. (Jeśli zachowanie z AbstractCollection
zostało wyrażone jako domyślne włączone Collection
, 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()
lub Comparator.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ć.