Java nie pozwala na wielokrotne dziedziczenie, ale umożliwia implementację wielu interfejsów. Czemu?
153
Java nie pozwala na wielokrotne dziedziczenie, ale umożliwia implementację wielu interfejsów. Czemu?
Odpowiedzi:
Ponieważ interfejsy określają tylko to, co robi klasa, a nie jak to robi.
Problem z dziedziczeniem wielokrotnym polega na tym, że dwie klasy mogą definiować różne sposoby robienia tego samego, a podklasa nie może wybrać, którą z nich wybrać.
źródło
Jeden z moich instruktorów w college'u wyjaśnił mi to w ten sposób:
Więc jednym z głównych problemów jest to, że jeśli masz dwie klasy nadrzędne, mogą one mieć różne implementacje tej samej funkcji - lub być może dwie różne funkcje o tej samej nazwie, jak w przykładzie mojego instruktora. Następnie musisz podjąć decyzję, której podklasy będzie używać Twoja podklasa. Z pewnością istnieją sposoby, aby sobie z tym poradzić - robi to C ++ - ale projektanci Javy uznali, że byłoby to zbyt skomplikowane.
Jednak z interfejsem opisujesz coś, co klasa jest w stanie zrobić, zamiast pożyczać metodę innej klasy, aby coś zrobić. Wiele interfejsów znacznie rzadziej powoduje skomplikowane konflikty, które należy rozwiązać, niż wiele klas nadrzędnych.
źródło
Ponieważ dziedziczenie jest nadużywane, nawet jeśli nie możesz powiedzieć „hej, ta metoda wygląda na użyteczną, rozszerzę również tę klasę”.
źródło
Odpowiedź na to pytanie leży w wewnętrznym działaniu kompilatora java (łańcuch konstruktora). Jeśli zobaczymy wewnętrzne działanie kompilatora java:
Po skompilowaniu wygląda to następująco:
kiedy rozszerzymy klasę i utworzymy jej obiekt, jeden łańcuch konstruktora będzie działał aż do
Object
klasy.Powyższy kod będzie działał poprawnie. ale jeśli mamy inną klasę o nazwie,
Car
która rozszerzaBank
i jedną klasę hybrydową (wielokrotne dziedziczenie) o nazwieSBICar
:W tym przypadku (SBICar) nie utworzy łańcucha konstruktora ( niejednoznaczność czasu kompilacji ).
W przypadku interfejsów jest to dozwolone, ponieważ nie możemy stworzyć z niego obiektu.
Aby poznać nową koncepcję
default
istatic
metodę, zapoznaj się z default w interfejsie .Mam nadzieję, że to rozwiąże Twoje pytanie. Dzięki.
źródło
Implementacja wielu interfejsów jest bardzo przydatna i nie sprawia większych problemów ani implementatorom języka, ani programistom. Więc to jest dozwolone. Wielokrotne dziedziczenie, choć jest również przydatne, może powodować poważne problemy dla użytkowników (straszny diament śmierci ). Większość czynności związanych z dziedziczeniem wielokrotnym można również wykonać za pomocą kompozycji lub klas wewnętrznych. Dlatego zabronione jest dziedziczenie wielokrotne, ponieważ przynosi więcej problemów niż korzyści.
źródło
ToyotaCar
iHybridCar
obie wywodziły się zCar
i nadpisałyCar.Drive
, i gdybyPriusCar
odziedziczyły oba, ale nie nadpisałybyDrive
, system nie miałby możliwości zidentyfikowania, coCar.Drive
powinien zrobić wirtualny . Interfejsy pozwalają uniknąć tego problemu, unikając powyższego warunku zapisanego kursywą.void UseCar(Car &foo)
; nie można oczekiwać, że będzie zawierał ujednoznacznienie międzyToyotaCar::Drive
iHybridCar::Drive
(ponieważ często nie powinien wiedzieć ani dbać o to, że te inne typy w ogóle istnieją ). Język mógłby, tak jak robi to C ++, wymagać, aby kod, któryToyotaCar &myCar
chce go przekazać,UseCar
musi najpierw rzutować na alboHybridCar
alboToyotaCar
, ale ponieważ ((Car) (HybridCar) myCar) .Drive` i robiłby((Car)(ToyotaCar)myCar).Drive
różne rzeczy, co oznaczałoby, że upcasts nie chroniły tożsamości.Dokładną odpowiedź na to zapytanie można znaleźć na stronie dokumentacji Oracle dotyczącej wielokrotnego dziedziczenia
Wielokrotne dziedziczenie stanu: możliwość dziedziczenia pól z wielu klas
Jeśli wielokrotne dziedziczenie jest dozwolone i podczas tworzenia obiektu przez utworzenie instancji tej klasy, obiekt ten odziedziczy pola ze wszystkich nadklas klasy. Spowoduje to dwa problemy.
Wielokrotne dziedziczenie implementacji: Możliwość dziedziczenia definicji metod z wielu klas
Problemy z tym podejściem: konflikty nazw i niejednoznaczność . Jeśli podklasa i nadklasa zawierają tę samą nazwę metody (i podpis), kompilator nie może określić, którą wersję wywołać.
Ale java obsługuje ten typ wielokrotnego dziedziczenia z domyślnymi metodami , które zostały wprowadzone od wydania Java 8. Kompilator Java udostępnia kilka reguł określających, której metody domyślnej używa dana klasa.
Zapoznaj się z poniższym postem SE, aby uzyskać więcej informacji na temat rozwiązywania problemu z diamentami:
Jakie są różnice między klasami abstrakcyjnymi a interfejsami w Javie 8?
Wielokrotne dziedziczenie typu: zdolność klasy do implementacji więcej niż jednego interfejsu.
Ponieważ interfejs nie zawiera zmiennych zmiennych, nie musisz martwić się o problemy wynikające z wielokrotnego dziedziczenia stanu.
źródło
Mówi się, że stan obiektów odnosi się do znajdujących się w nim pól i stałoby się niejednoznaczne, gdyby dziedziczono zbyt wiele klas. Tutaj jest link
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
źródło
Java obsługuje wielokrotne dziedziczenie tylko przez interfejsy. Klasa może implementować dowolną liczbę interfejsów, ale może rozszerzać tylko jedną klasę.
Dziedziczenie wielokrotne nie jest obsługiwane, ponieważ prowadzi do śmiertelnego problemu z diamentami. Jednak można go rozwiązać, ale prowadzi to do złożonego systemu, więc twórcy Javy porzucili wielokrotne dziedziczenie.
W białej księdze zatytułowanej „Java: an Overview” Jamesa Goslinga w lutym 1995 r. ( Link ) podano, dlaczego dziedziczenie wielokrotne nie jest obsługiwane w Javie.
Według Gosling:
źródło
Z tego samego powodu C # nie zezwala na wielokrotne dziedziczenie, ale umożliwia implementację wielu interfejsów.
Lekcja wyciągnięta z C ++ z dziedziczeniem wielokrotnym była taka, że prowadziło to do większej liczby problemów, niż było to warte.
Interfejs jest kontraktem rzeczy, które Twoja klasa musi zaimplementować. Nie zyskujesz żadnej funkcjonalności z interfejsu. Dziedziczenie pozwala na dziedziczenie funkcjonalności klasy nadrzędnej (w przypadku dziedziczenia wielokrotnego, co może być bardzo mylące).
Zezwolenie na wiele interfejsów pozwala używać wzorców projektowych (takich jak adapter) do rozwiązywania tych samych typów problemów, które można rozwiązać przy użyciu wielokrotnego dziedziczenia, ale w znacznie bardziej niezawodny i przewidywalny sposób.
źródło
D1
iD2
oba dziedziczą poB
i każdy przesłaniają funkcjęf
, a jeśliobj
jest wystąpieniem typu,S
który dziedziczy z obuD1
iD2
nie przesłaniaf
, to rzutowanie odwołania doS
doD1
powinno dać coś, cof
używaD1
przesłonięcia, a rzutowanie naB
nie powinno Zmień to. Podobnie rzutowanie odwołaniaS
doD2
powinno dać coś, cof
używaD2
override, a rzutowanie naB
nie powinno tego zmieniać. Jeśli język nie musiał pozwalać na dodawanie wirtualnych członków ...Ponieważ ten temat nie jest zamknięty, opublikuję tę odpowiedź, mam nadzieję, że pomoże to komuś zrozumieć, dlaczego java nie zezwala na wielokrotne dziedziczenie.
Rozważ następującą klasę:
W tym przypadku klasa Abc niczego nie rozszerza, prawda? Nie tak szybko, ta klasa niejawnie rozszerza klasę Object, klasę bazową, która pozwala wszystkim działać w Javie. Wszystko jest przedmiotem.
Jeśli spróbujesz użyć klasy powyżej zobaczysz, że Twój IDE pozwalają na stosowanie metod takich jak:
equals(Object o)
,toString()
, etc, ale nie deklarują te metody, wyszli z klasy bazowejObject
Możesz spróbować:
To jest w porządku, ponieważ twoja klasa nie będzie niejawnie rozszerzać,
Object
ale będzie rozszerzać,String
ponieważ to powiedziałeś. Rozważ następującą zmianę:Teraz Twoja klasa zawsze będzie zwracać „hello”, jeśli wywołasz toString ().
Teraz wyobraź sobie następującą klasę:
Ponownie klasa
Flyer
niejawna rozszerza Object, który ma metodętoString()
, każda klasa będzie miała tę metodę, ponieważ wszystkie one rozszerzają sięObject
pośrednio, więc jeśli wywołasztoString()
fromBird
, z którejtoString()
javy musiałby skorzystać? OdAbc
lubFlyer
? Stanie się tak z każdą klasą, która próbuje rozszerzyć dwie lub więcej klas, aby uniknąć tego rodzaju „kolizji metod”, zbudowali ideę interfejsu , w zasadzie można by pomyśleć, że jest to klasa abstrakcyjna, która nie rozszerza Object w sposób pośredni . Ponieważ są abstrakcyjne , będą musiały zostać zaimplementowane przez klasę, która jest obiektem (nie możesz samodzielnie instancjonować interfejsu, muszą być zaimplementowane przez klasę), więc wszystko będzie nadal działać poprawnie.Aby odróżnić klasy od interfejsów, słowo kluczowe implements zostało zarezerwowane tylko dla interfejsów.
Możesz zaimplementować dowolny interfejs w tej samej klasie, ponieważ domyślnie nic nie rozszerza (ale możesz utworzyć interfejs, który rozszerza inny interfejs, ale ponownie, interfejs „ojca” nie rozszerza Object ”), więc interfejs jest tylko interfejs i nie będą cierpieć z powodu " kolizji sygnatur metod ", jeśli to zrobią, kompilator wyśle ostrzeżenie i będziesz musiał tylko zmienić sygnaturę metody, aby to naprawić (podpis = nazwa metody + parametry + typ zwrotu) .
źródło
Ponieważ interfejs to tylko umowa. Klasa jest w rzeczywistości pojemnikiem na dane.
źródło
Na przykład dwie klasy A, B mające tę samą metodę m1 (). A klasa C rozszerza zarówno A, B.
Teraz klasa C przeszuka definicję m1. Najpierw wyszuka w klasie, jeśli nie znalazł, a następnie sprawdzi klasę rodziców. Zarówno A, jak i B mają definicję. Więc tutaj pojawia się niejednoznaczność, którą definicję należy wybrać. Więc JAVA NIE WSPIERA WIELU DZIEDZICTWA.
źródło
Java nie obsługuje dziedziczenia wielokrotnego z dwóch powodów:
Object
klasy. Gdy dziedziczy z więcej niż jednej superklasy, podklasa uzyskuje niejednoznaczność, aby uzyskać właściwość klasy Object.super()
konstruktora klasy kolacji. Jeśli klasa ma więcej niż jedną superklasę, jest zdezorientowana.Więc gdy jedna klasa pochodzi z więcej niż jednej superklasy, otrzymujemy błąd czasu kompilacji.
źródło
Weźmy na przykład przypadek, w którym klasa A ma metodę getSomething, a klasa B ma metodę getSomething, a klasa C rozszerza A i B. Co by się stało, gdyby ktoś nazwał C.getSomething? Nie ma sposobu, aby określić, którą metodę wywołać.
Interfejsy w zasadzie po prostu określają, jakie metody musi zawierać klasa implementująca. Klasa, która implementuje wiele interfejsów, oznacza po prostu, że klasa musi implementować metody ze wszystkich tych interfejsów. Co nie spowoduje żadnych problemów opisanych powyżej.
źródło
Rozważmy scenariusz, w którym Test1, Test2 i Test3 to trzy klasy. Klasa Test3 dziedziczy klasy Test2 i Test1. Jeśli klasy Test1 i Test2 mają tę samą metodę i wywołasz ją z obiektu klasy potomnej, wywołanie metody klasy Test1 lub Test2 będzie niejednoznaczne, ale nie ma takiej niejednoznaczności dla interfejsu, jak w interfejsie nie ma implementacji.
źródło
Java nie obsługuje dziedziczenia wielokrotnego, wielościeżkowego i hybrydowego z powodu problemu z niejednoznacznością:
dziedziczenie wielokrotne
źródło
w prosty sposób wszyscy wiemy, możemy dziedziczyć (rozszerzać) jedną klasę, ale możemy zaimplementować tak wiele interfejsów ... to dlatego, że w interfejsach nie podajemy implementacji, po prostu mówimy o funkcjonalności. załóżmy, że jeśli java może rozszerzyć tak wiele klas, a te mają te same metody .. w tym miejscu, jeśli spróbujemy wywołać metodę superklasy w podklasie, jaką metodę ma uruchomić?, kompilator jest zagmatwany przykład: - spróbuj wielu rozszerzeń, ale w interfejsy te metody nie mają treści, powinniśmy zaimplementować je w podklasie .. spróbuj użyć wielu implementacji, więc nie martw się.
źródło
* To prosta odpowiedź, ponieważ jestem początkującym użytkownikiem języka Java *
Rozważmy istnieją trzy klasy
X
,Y
aZ
.Więc dziedziczymy jak
X extends Y, Z
I obaY
iZ
mamy metodęalphabet()
z tym samym typem zwracania i argumentami. Ta metodaalphabet()
wY
powiada wyświetlić pierwszy alfabet i metody alfabetu wZ
mówi wyświetlania ostatniego alfabetu . Tak więc pojawia się niejednoznaczność, gdyalphabet()
jest wywoływany przezX
. Czy mówi się o wyświetlaniu pierwszego czy ostatniego alfabetu ??? Dlatego java nie obsługuje dziedziczenia wielokrotnego. W przypadku interfejsów rozważY
iZ
jako interfejsy. Więc oba będą zawierać deklarację metody,alphabet()
ale nie definicję. Nie powie, czy wyświetlić pierwszy czy ostatni alfabet, czy cokolwiek, ale po prostu zadeklaruje metodęalphabet()
. Nie ma więc powodu, by podnosić tę niejednoznaczność. Możemy zdefiniować metodę za pomocą dowolnego elementu wewnątrz klasyX
.Krótko mówiąc, definicja interfejsów jest wykonywana po implementacji, więc nie ma zamieszania.
źródło