Java 8 pozwala na domyślną implementację metod w interfejsach zwanych metodami domyślnymi .
Nie jestem pewien interface default method
, kiedy powinienem użyć tego rodzaju zamiast abstract class
(z abstract method(s)
).
Kiedy zatem należy stosować interfejs z metodami domyślnymi, a kiedy należy stosować klasę abstrakcyjną (z metodami abstrakcyjnymi)? Czy klasy abstrakcyjne są nadal przydatne w tym scenariuszu?
java
interface
java-8
abstract-class
default-method
Narendra Pathai
źródło
źródło
Odpowiedzi:
W klasach abstrakcyjnych jest znacznie więcej niż w domyślnych implementacjach metod (takich jak stan prywatny), ale od wersji Java 8, kiedy tylko masz do wyboru jedną z nich, powinieneś użyć metody defender (aka.
default
) W interfejsie.Ograniczeniem metody domyślnej jest to, że można ją wdrożyć tylko w zakresie wywołań innych metod interfejsu, bez odniesienia do stanu konkretnej implementacji. Tak więc głównym przypadkiem użycia są metody wyższego poziomu i wygody.
Zaletą tej nowej funkcji jest to, że zanim wcześniej zmuszono Cię do użycia abstrakcyjnej klasy dla metod wygody, ograniczając w ten sposób implementator do pojedynczego dziedziczenia, teraz możesz mieć naprawdę czysty projekt z samym interfejsem i minimalną implementacją wysiłek wymuszony na programatorze.
Oryginalną motywacją do wprowadzenia
default
metod do Java 8 była chęć rozszerzenia interfejsów Framework Framework o metody zorientowane na lambda bez przerywania jakichkolwiek istniejących implementacji. Chociaż jest to bardziej odpowiednie dla autorów bibliotek publicznych, ta sama funkcja może być przydatna również w twoim projekcie. Masz jedno scentralizowane miejsce, w którym możesz dodać nową wygodę, i nie musisz polegać na tym, jak wygląda reszta hierarchii typów.źródło
Istnieje kilka różnic technicznych. Klasy abstrakcyjne mogą nadal robić więcej w porównaniu z interfejsami Java 8:
Pod względem koncepcyjnym głównym celem metod obrońcy jest zgodność wsteczna po wprowadzeniu nowych funkcji (jako funkcji lambda) w Javie 8.
źródło
public static final
pól interfejsu jako „stan”. Tastatic
część oznacza, że wcale nie są związane z konkretną instancją. Są one przypisywane przy tworzeniu instancji klasy , co nie jest tym samym, co po utworzeniu instancji .Jest to opisane w tym artykule . Pomyśl o
forEach
kolekcjach.źródło
AbstractList::forEach
rzucanieUnsupportedOperationException
.Te dwa są całkiem różne:
Domyślne metody to dodawanie funkcjonalności zewnętrznej do istniejących klas bez zmiany ich stanu.
A klasy abstrakcyjne są normalnym rodzajem dziedziczenia, są normalnymi klasami, które mają być rozszerzone.
źródło
Jak opisano w tym artykule,
Klasy abstrakcyjne a interfejsy w Javie 8
źródło
Jeśli chodzi o zapytanie
Dokumentacja Java zapewnia doskonałą odpowiedź.
Klasy abstrakcyjne w porównaniu do interfejsów:
Przypadki użycia dla każdego z nich zostały wyjaśnione w poniższym poście SE:
Jaka jest różnica między interfejsem a klasą abstrakcyjną?
Tak. Nadal są przydatne. Mogą zawierać niestatyczne, niekończące się metody i atrybuty ( chronione, prywatne oprócz publicznego ), co nie jest możliwe nawet w przypadku interfejsów Java-8.
źródło
Ilekroć mamy wybór między klasą abstrakcyjną a interfejsem, zawsze (prawie) powinniśmy preferować metody domyślne (znane również jako obrońca lub rozszerzenia wirtualne).
Collection and AbstractCollection
. Teraz powinniśmy wdrożyć metody w samym interfejsie, aby zapewnić domyślną funkcjonalność. Klasy, które implementują interfejs, mogą zastąpić metody lub odziedziczyć domyślną implementację.Innym ważnym zastosowaniem metod domyślnych jest
interface evolution
. Załóżmy, że miałem klasową piłkę jako:public class Ball implements Collection { ... }
Teraz w Javie 8 wprowadzono nową funkcję. Możemy uzyskać strumień, używając
stream
metody dodanej do interfejsu. Gdybystream
nie była to metoda domyślna, wszystkie implementacjeCollection
interfejsu zostałyby zepsute, ponieważ nie wdrażałyby tej nowej metody. Dodanie niestandardowej metody do interfejsu nie jestsource-compatible
.Załóżmy jednak, że nie dokonujemy ponownej kompilacji klasy i nie używamy starego pliku jar zawierającego tę klasę
Ball
. Klasa ładuje się dobrze bez tej brakującej metody, można tworzyć instancje i wygląda na to, że wszystko działa dobrze. ALE jeśli program wywołastream
metodę na przykładBall
otrzymamyAbstractMethodError
. Ustawienie domyślnej metody rozwiązało oba problemy.źródło
Domyślne metody interfejsu Java umożliwiają ewolucję interfejsu .
Biorąc pod uwagę istniejący interfejs, jeśli chcesz dodać do niego metodę bez naruszania binarnej zgodności ze starszymi wersjami interfejsu, masz dwie opcje: dodaj domyślną lub statyczną metodę. Rzeczywiście, każda abstrakcyjna metoda dodana do interfejsu musiałaby być zaimplementowana przez klasy lub interfejsy implementujące ten interfejs.
Metoda statyczna jest unikalna dla klasy. Domyślna metoda jest unikalna dla instancji klasy.
Jeśli dodasz domyślną metodę do istniejącego interfejsu, klasy i interfejsy, które implementują ten interfejs, nie muszą go implementować. Mogą
Więcej na ten temat tutaj .
źródło
Chociaż jest to stare pytanie, pozwólcie, że podzielę się również swoimi uwagami.
Klasa abstrakcyjna: Wewnątrz klasy abstrakcyjnej możemy zadeklarować zmienne instancji, które są wymagane dla klasy potomnej
Interfejs: Wewnątrz interfejsu każda zmienna jest zawsze publiczna statyczna i ostatecznie nie możemy zadeklarować zmiennych instancji
Klasa abstrakcyjna: Klasa abstrakcyjna może mówić o stanie obiektu
Interfejs: Interfejs nigdy nie może mówić o stanie obiektu
Klasa abstrakcyjna: Wewnątrz Klasa abstrakcyjna możemy deklarować konstruktory
Interfejs: Wewnątrz interfejsu nie możemy deklarować konstruktorów, ponieważ celem
konstruktorów jest inicjowanie zmiennych instancji. Więc jaka jest potrzeba konstruktora, jeśli nie możemy mieć zmiennych instancji w interfejsach .
Klasa abstrakcyjna: Wewnątrz klasy abstrakcyjnej możemy zadeklarować bloki instancji i statyczne
Interfejs: Interfejsy nie mogą mieć bloków instancji i bloków statycznych.
Klasa abstrakcyjna: Klasa abstrakcyjna nie może odwoływać się do wyrażenia lambda
Interfejsy: Interfejsy z jedną metodą abstrakcyjną mogą odwoływać się do wyrażenia lambda
Klasa abstrakcyjna : Wewnątrz klasy abstrakcyjnej możemy przesłonić metody OBJECT CLASS
Interfejsy: Nie możemy przesłonić metod OBJECT CLASS w interfejsach.
Zakończę notatką, że:
Domyślne koncepcje metod / statyczne koncepcje metod w interfejsie zostały wprowadzone tylko w celu zapisania klas implementacji, ale nie w celu zapewnienia sensownej użytecznej implementacji. Metody domyślne / metody statyczne są rodzajem fikcyjnej implementacji, „jeśli chcesz, możesz ich użyć lub możesz je zastąpić (w przypadku metod domyślnych) w klasie implementacji”. Dzięki temu oszczędzamy nam wdrażania nowych metod w klasach implementacji za każdym razem, gdy nowe metody w interfejsach są dodane. Dlatego interfejsy nigdy nie mogą być równe klasom abstrakcyjnym.
źródło
Reguła Remi Forax jest taka, że nie projektujesz klasami abstrakcyjnymi. Projektujesz swoją aplikację za pomocą interfejsów . Niezależnie od wersji Java, niezależnie od języka. Jest on wspierany przez I zasady segregacji nterface w SOL I D zasad.
Później możesz użyć klas Abstract do podziału kodu na czynniki. Teraz w Javie 8 możesz to zrobić bezpośrednio w interfejsie. To jest obiekt, nie więcej.
źródło
Kompatybilność wsteczna: wyobraź sobie, że twój interfejs jest implementowany przez setki klas, modyfikacja tego interfejsu zmusi wszystkich użytkowników do implementacji nowo dodanej metody, nawet jeśli może nie być niezbędna dla wielu innych klas, które implementują twój interfejs, plus pozwala na twój interfejs być funkcjonalnym interfejsem
Fakty i ograniczenia:
1-może być zadeklarowany tylko w interfejsie, a nie w klasie lub klasie abstrakcyjnej.
2-musi zapewnić ciało
3-Nie zakłada się, że jest abstrakcyjny, jak inne normalne metody stosowane w interfejsie.
źródło
W Javie 8 interfejs wygląda jak klasa abstrakcyjna, chociaż mogą występować pewne różnice, takie jak:
1) Klasy abstrakcyjne to klasy, więc nie są ograniczone do innych ograniczeń interfejsu w Javie, np. Klasa abstrakcyjna może mieć stan , ale nie można mieć stanu na interfejsie w Javie.
2) Kolejną różnicą semantyczną między interfejsem z metodami domyślnymi a klasą abstrakcyjną jest to, że można definiować konstruktory wewnątrz klasy abstrakcyjnej , ale nie można definiować konstruktora w interfejsie w Javie
źródło
Domyślne metody w interfejsie Java powinny być używane raczej do zapewnienia fałszywej implementacji funkcji, oszczędzając w ten sposób każdą klasę implementacyjną tego interfejsu przed bólem wynikającym z deklarowania wszystkich metod abstrakcyjnych, nawet jeśli chcą one mieć do czynienia tylko z jedną. Metody domyślne w interfejsie są więc w pewnym sensie zastępstwem koncepcji klas adapterów.
Metody w klasie abstrakcyjnej powinny jednak dawać sensowną implementację, którą każda klasa potomna powinna zastąpić tylko w razie potrzeby zastąpienia wspólnej funkcjonalności.
źródło
Jak wspomniano w innych odpowiedziach, dodano możliwość dodania implementacji do interfejsu w celu zapewnienia kompatybilności wstecznej w ramach kolekcji. Twierdziłbym, że zapewnienie kompatybilności wstecznej jest potencjalnie jedynym dobrym powodem dodania implementacji do interfejsu.
W przeciwnym razie, jeśli dodasz implementację do interfejsu, łamiesz podstawowe prawo, dlaczego interfejsy zostały dodane w pierwszej kolejności. Java jest jednym językiem dziedziczenia, w przeciwieństwie do C ++, który pozwala na wielokrotne dziedziczenie. Interfejsy zapewniają korzyści związane z pisaniem w języku obsługującym wielokrotne dziedziczenie bez wprowadzania problemów związanych z wielokrotnym dziedziczeniem.
Mówiąc dokładniej, Java pozwala tylko na pojedyncze dziedziczenie implementacji, ale pozwala na wielokrotne dziedziczenie interfejsów. Na przykład następujący kod Java jest prawidłowy:
MyObject
dziedziczy tylko jedną implementację, ale dziedziczy trzy umowy.Java przekazała wielokrotne dziedziczenie implementacji, ponieważ wielokrotne dziedziczenie implementacji wiąże się z wieloma drażliwymi problemami, które są poza zakresem tej odpowiedzi. Dodano interfejsy, aby umożliwić wielokrotne dziedziczenie umów (inaczej interfejsy) bez problemów wielokrotnego dziedziczenia implementacji.
Aby wesprzeć moją tezę, oto cytat Kena Arnolda i Jamesa Goslinga z książki The Java Programming Language, wydanie 4 :
źródło
Najpierw pomyśl o zasadzie otwarcia / zamknięcia. Domyślne metody w interfejsach NARUSZAJĄ to. Jest to zła funkcja w Javie. Zachęca do złego projektowania, złej architektury, niskiej jakości oprogramowania. Sugerowałbym, aby całkowicie unikać używania domyślnych metod.
Zadaj sobie kilka pytań: Dlaczego nie możesz umieścić swoich metod w klasie abstrakcyjnej? Czy potrzebujesz więcej niż jednej klasy abstrakcyjnej? Zastanów się, za co odpowiada twoja klasa. Czy jesteś pewien, że wszystkie metody, które zamierzasz zastosować w jednej klasie, naprawdę spełniają ten sam cel? Być może rozróżnisz kilka celów, a następnie podzielisz swoją klasę na kilka klas, dla każdego celu własną klasę.
źródło