Przez dziesięciolecia to było tak, że interfejsy były tylko tylko (tylko) dla określenia podpisy metod. Powiedziano nam, że to „właściwy sposób na robienie rzeczy”.
Potem pojawiła się Java 8 i powiedziała:
Teraz możesz zdefiniować domyślne metody. Muszę uciekać, pa.
Jestem ciekawy, w jaki sposób jest to trawione zarówno przez doświadczonych programistów Java, jak i tych, którzy zaczęli go ostatnio (w ostatnich latach) rozwijać. Zastanawiam się także, jak to pasuje do ortodoksji i praktyki Java.
Buduję trochę kodu eksperymentalnego i kiedy robiłem trochę refaktoryzacji, skończyłem z interfejsem, który po prostu rozszerza standardowy interfejs (Iterable) i dodaje dwie domyślne metody. I będę szczera, czuję się z tym cholernie dobrze.
Wiem, że jest to trochę otwarte, ale teraz, gdy było już trochę czasu, aby Java 8 mogła być używana w prawdziwych projektach, czy istnieje ortodoksja dotycząca korzystania z domyślnych metod? To, co najczęściej widzę, kiedy są omawiane, dotyczy sposobu dodawania nowych metod do interfejsu bez niszczenia istniejących konsumentów. Ale co z użyciem tego od samego początku, jak w przykładzie podanym powyżej. Czy ktoś napotkał jakiekolwiek problemy z implementacją w swoich interfejsach?
źródło
java.util.function.Function
użycie metod domyślnych w zupełnie nowym interfejsie.Odpowiedzi:
Świetnym przykładem użycia są interfejsy „dźwigniowe”: interfejsy, które mają tylko niewielką liczbę abstrakcyjnych metod (najlepiej 1), ale zapewniają wiele „dźwigni”, ponieważ zapewniają wiele funkcji: tylko Ty trzeba zaimplementować 1 metodę w swojej klasie, ale uzyskać wiele innych metod „za darmo”. Pomyśl o interfejsie zbiórki, na przykład, za pomocą jednego abstrakcyjnego
foreach
sposobu idefault
metod, takich jakmap
,fold
,reduce
,filter
,partition
,groupBy
,sort
,sortBy
, itd.Oto kilka przykładów. Zacznijmy
java.util.function.Function<T, R>
. Ma jedną abstrakcyjną metodęR apply<T>
. I ma dwie domyślne metody, które pozwalają komponować funkcję z inną funkcją na dwa różne sposoby, przed lub po. Obie te metody kompozycji są implementowane przy użyciuapply
:Możesz także utworzyć interfejs dla porównywalnych obiektów, coś takiego:
Lub wyjątkowo uproszczona struktura kolekcji, w której zwracane są wszystkie operacje kolekcji
Collection
, niezależnie od tego, jaki był oryginalny typ:Staje się to bardzo interesujące w połączeniu z lambdas, ponieważ taki interfejs „dźwigniowy” może być zaimplementowany przez lambda (jest to interfejs SAM).
Jest to ten sam przypadek użycia, dla którego metody C zostały dodane w C♯, ale metody domyślne mają jedną wyraźną zaletę: są to „właściwe” metody instancji, co oznacza, że mają dostęp do prywatnych szczegółów implementacyjnych interfejsu (
private
nadchodzą metody interfejsu w Javie 9), podczas gdy metody rozszerzeń są jedynie cukrem syntaktycznym dla metod statycznych.Gdyby Java kiedykolwiek otrzymała interfejs wstrzykiwania, pozwoliłaby również na bezpieczne, modułowe, łatanie małp w poprawnym typie. Byłoby to bardzo interesujące dla implementatorów języka na JVM: na przykład JRuby dziedziczy lub otacza klasy Java, aby zapewnić im dodatkową semantykę Ruby, ale idealnie chcą używać tych samych klas. Dzięki interfejsowi wstrzykiwania i metodom domyślnym mogą wstrzykiwać np.
RubyObject
Interfejsjava.lang.Object
, tak aby JavaObject
i RubyObject
były dokładnie tym samym .źródło
Comparable
interfejs z abstrakcyjnejcompareTo
metody i domyślnielessThan
,lessThanOrEqual
,greaterThan
,greaterThanOrEqual
,isBetween
, orazclamp
sposoby, wszystko realizowane w kategoriachcompareTo
. Lub spójrz tylkojava.util.function.Function
: maapply
metodę abstrakcyjną i dwie domyślne metody kompozycji, obie zaimplementowane pod względemapply
. Próbowałem podać przykładCollection
interfejsu, ale uzyskanie bezpieczeństwa typu jest trudne i zbyt długie, aby odpowiedzieć na tę odpowiedź - postaram się dać szansę wersji nie bezpiecznej i nie zachowującej typu. Bądźcie czujni.