Dlaczego w Javie nie ma dziedziczenia wielokrotnego, ale dozwolone jest wdrażanie wielu interfejsów?

153

Java nie pozwala na wielokrotne dziedziczenie, ale umożliwia implementację wielu interfejsów. Czemu?

abson
źródło
1
Zredagowałem tytuł pytania, aby był bardziej opisowy.
Bozho
4
Co ciekawe, w JDK 8 pojawią się metody rozszerzające, które pozwolą na zdefiniowanie implementacji metod interfejsu. Reguły zostały zdefiniowane tak, aby rządziły dziedziczeniem wielokrotnym zachowań, ale nie stanu (co, jak rozumiem, jest bardziej problematyczne .
Edwin Dalorzo
1
Zanim zaczniecie tracić czas na odpowiedzi, które mówią tylko „Jak java osiąga wielokrotne dziedziczenie” ... Proponuję, abyście przejrzeli odpowiedź @Prabal Srivastava, która logicznie przedstawia, co musi się dziać wewnętrznie, aby nie zezwalać na takie zajęcia. i zezwalaj tylko interfejsom na wielokrotne dziedziczenie.
Yo Apps

Odpowiedzi:

228

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ć.

Bozho
źródło
8
Kiedyś robiłem C ++ i kilka razy napotkałem dokładnie ten sam problem. Niedawno czytałem o Scali, która ma „cechy”, które wydają mi się czymś pomiędzy sposobem „C ++” a sposobem „Java” robienia rzeczy.
Niels Basjes
2
W ten sposób unikasz „problemu z diamentami”: en.wikipedia.org/wiki/Diamond_problem#The_diamond_problem
Nick L.,
6
Od Java 8 można zdefiniować dwie identyczne metody domyślne, po jednej w każdym interfejsie. Jeśli zaimplementujesz oba interfejsy w swojej klasie, musisz nadpisać tę metodę w samej klasie, patrz: docs.oracle.com/javase/tutorial/java/IandI/…
bobbel
6
Ta odpowiedź nie jest dokładna. Problem nie polega na określeniu, jak i Java 8 ma to udowodnić. W Javie 8 dwa super interfejsy mogą zadeklarować tę samą metodę z różnymi implementacjami, ale nie stanowi to problemu, ponieważ metody interfejsów są wirtualne , więc możesz je po prostu nadpisać, a problem zostanie rozwiązany. Prawdziwy problem jest o dwuznaczności w atrybutach , ponieważ nie można rozwiązać tę niejasność nadpisując atrybut (atrybuty nie są wirtualne).
Alex Oliveira,
1
Co rozumiesz przez „atrybuty”?
Bozho
96

Jeden z moich instruktorów w college'u wyjaśnił mi to w ten sposób:

Załóżmy, że mam jedną klasę, czyli Toster, i inną, czyli NuclearBomb. Oboje mogą mieć ustawienie „ciemności”. Oba mają metodę on (). (Jedna ma off (), druga nie.) Jeśli chcę stworzyć klasę będącą podklasą obu tych ... jak widzisz, jest to problem, który może naprawdę wybuchnąć mi w twarz .

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.

Syntaktyczny
źródło
5
Dzięki, NomeN. Źródłem cytatu był doktorant Brendan Burns, który w tym czasie był również opiekunem repozytorium źródeł Quake 2 o otwartym kodzie źródłowym. Domyśl.
Syntactic
4
Problem z tą analogią polega na tym, że jeśli tworzysz podklasę bomby atomowej i tostera, „toster nuklearny” rozsadziłby się po użyciu.
101100
20
Jeśli ktoś zdecyduje się zmieszać bombę atomową i toster, zasługuje na to, by bomba wybuchła mu w twarz. Cytat jest błędnym rozumowaniem
masoud
1
Ten sam język programowania dopuszcza wielokrotne dziedziczenie „interfejsu”, więc to „uzasadnienie” nie ma zastosowania.
curiousguy
24

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ę”.

public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter, 
             AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Michael Borgwardt
źródło
Czy możesz wyjaśnić, dlaczego mówisz, że dziedziczenie jest nadużywane? Stworzenie klasy boga jest dokładnie tym, co chcę zrobić! Spotykam się z wieloma osobami, które zajmują się dziedziczeniem pojedynczym, tworząc klasy „Narzędzia”, które mają metody statyczne.
Duncan Calvert,
9
@DuncanCalvert: Nie, nie chcesz tego robić, nie jeśli ten kod będzie kiedykolwiek wymagał konserwacji. Wiele metod statycznych mija się z celem OO, ale nadmierne dziedziczenie wielokrotne jest znacznie gorsze, ponieważ całkowicie tracisz orientację, który kod jest używany i gdzie, a także czym jest pojęciowo klasa. Obaj próbują rozwiązać problem „jak mogę użyć tego kodu tam, gdzie go potrzebuję”, ale jest to prosty, krótkotrwały problem. O wiele trudniejszym długoterminowym problemem rozwiązanym przez właściwy projekt OO jest „jak zmienić ten kod bez przerywania programu w 20 różnych miejscach w nieprzewidziany sposób?”
Michael Borgwardt
2
@DuncanCalvert: i rozwiązujesz to, mając klasy o wysokiej spójności i niskim sprzężeniu, co oznacza, że ​​zawierają one fragmenty danych i kodu, które intensywnie ze sobą współdziałają, ale współdziałają z resztą programu tylko za pośrednictwem małego, prostego publicznego interfejsu API. Następnie możesz myśleć o nich w kategoriach tego interfejsu API zamiast wewnętrznych szczegółów, co jest ważne, ponieważ ludzie mogą jednocześnie pamiętać tylko o ograniczonej liczbie szczegółów.
Michael Borgwardt
18

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:

public class Bank {
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI extends Bank{
 public void printBankBalance(){
    System.out.println("20k");
  }
}

Po skompilowaniu wygląda to następująco:

public class Bank {
  public Bank(){
   super();
  }
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI extends Bank {
 SBI(){
   super();
 }
 public void printBankBalance(){
    System.out.println("20k");
  }
}

kiedy rozszerzymy klasę i utworzymy jej obiekt, jeden łańcuch konstruktora będzie działał aż do Objectklasy.

Powyższy kod będzie działał poprawnie. ale jeśli mamy inną klasę o nazwie, Carktóra rozszerza Banki jedną klasę hybrydową (wielokrotne dziedziczenie) o nazwie SBICar:

class Car extends Bank {
  Car() {
    super();
  }
  public void run(){
    System.out.println("99Km/h");
  }
}
class SBICar extends Bank, Car {
  SBICar() {
    super(); //NOTE: compile time ambiguity.
  }
  public void run() {
    System.out.println("99Km/h");
  }
  public void printBankBalance(){
    System.out.println("20k");
  }
}

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ę defaulti staticmetodę, zapoznaj się z default w interfejsie .

Mam nadzieję, że to rozwiąże Twoje pytanie. Dzięki.

Prabal Srivastava
źródło
7

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.

Tadeusz Kopeć
źródło
Jaki jest problem z „diamentem śmierci”?
curiousguy
2
@curiousguy Zawierający więcej niż jeden podobiekt tej samej klasy bazowej, niejednoznaczność (przesłonięcie, z którego korzysta klasa bazowa), skomplikowane zasady rozwiązywania takiej niejednoznaczności.
Tadeusz Kopeć
@curiousguy: Jeśli struktura przewiduje, że rzutowanie odwołania do obiektu na odniesienie typu podstawowego będzie zachowywać tożsamość, wówczas każda instancja obiektu musi mieć dokładnie jedną implementację dowolnej metody klasy bazowej. Gdyby ToyotaCari HybridCarobie wywodziły się z Cari nadpisały Car.Drive, i gdyby PriusCarodziedziczyły oba, ale nie nadpisałybyDrive , system nie miałby możliwości zidentyfikowania, co Car.Drivepowinien zrobić wirtualny . Interfejsy pozwalają uniknąć tego problemu, unikając powyższego warunku zapisanego kursywą.
supercat
1
@supercat „system nie byłby w stanie zidentyfikować, co powinien zrobić wirtualny Car.Drive”. <- Lub może po prostu dać błąd kompilacji i sprawić, że wybierzesz jeden jawnie, tak jak robi to C ++.
Chris Middleton
1
@ChrisMiddleton: metoda void UseCar(Car &foo); nie można oczekiwać, że będzie zawierał ujednoznacznienie między ToyotaCar::Drivei HybridCar::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óry ToyotaCar &myCarchce go przekazać, UseCarmusi najpierw rzutować na albo HybridCaralbo ToyotaCar, 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.
supercat
6

Dokładną odpowiedź na to zapytanie można znaleźć na stronie dokumentacji Oracle dotyczącej wielokrotnego dziedziczenia

  1. Wielokrotne dziedziczenie stanu: możliwość dziedziczenia pól z wielu klas

    Jednym z powodów, dla których język programowania Java nie pozwala na rozszerzenie więcej niż jednej klasy, jest uniknięcie problemów związanych z wielokrotnym dziedziczeniem stanu, czyli możliwością 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.

    1. Co się stanie, jeśli metody lub konstruktory z różnych superklas utworzą wystąpienie tego samego pola?
    2. Która metoda lub konstruktor będzie mieć pierwszeństwo?
  2. 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?

  3. 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.

Ravindra babu
źródło
4

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:

„JAVA pomija wiele rzadko używanych, słabo zrozumiałych, zagmatwanych funkcji C ++, które z naszego doświadczenia przynoszą więcej smutku niż korzyści. Obejmuje to przede wszystkim przeciążanie operatorów (chociaż ma przeciążanie metod), wielokrotne dziedziczenie i rozległe automatyczne wymuszanie”.

Praveen Kumar
źródło
link nie jest dostępny. Prosimy o sprawdzenie i zaktualizowanie go.
MashukKhan
3

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.

Justin Niessner
źródło
10
C # nie ma wielokrotnego dziedziczenia właśnie dlatego, że Java na to nie zezwala. Został zaprojektowany znacznie później niż Java. Myślę, że głównym problemem związanym z dziedziczeniem wielokrotnym był sposób, w jaki uczono ludzi korzystania z niego na prawo i lewo. Pomysł, że delegowanie w większości przypadków jest znacznie lepszą alternatywą, po prostu nie istniał na początku i połowie lat dziewięćdziesiątych. Stąd pamiętam przykłady z podręczników, kiedy samochód jest kołem i drzwiami i przednią szybą, a samochód zawiera koła, drzwi i przednią szybę. Tak więc jedyne dziedzictwo w Javie było odruchową reakcją na tę rzeczywistość.
Alexander Pogrebnyak,
2
@AlexanderPogrebnyak: Wybierz dwie z następujących trzech: (1) Zezwalaj na rzutowanie zachowujące tożsamość z odwołania do podtypu do odwołania do nadtypu; (2) Zezwól klasie na dodawanie wirtualnych publicznych członków bez ponownej kompilacji klas pochodnych; (3) Zezwalaj klasie na niejawne dziedziczenie wirtualnych elementów członkowskich z wielu klas podstawowych bez konieczności ich jawnego określania. Nie sądzę, aby jakikolwiek język był w stanie poradzić sobie ze wszystkimi trzema z powyższych. Java wybrała pierwsze i drugie, a C # poszedł w ich ślady. Uważam, że C ++ przyjmuje tylko # 3. Osobiście uważam, że # 1 i # 2 są bardziej przydatne niż # 3, ale inne mogą się różnić.
supercat
@supercat „Nie sądzę, aby jakikolwiek język mógł zarządzać wszystkimi trzema z powyższych” - jeśli przesunięcia elementów składowych danych są określane w czasie wykonywania, a nie w czasie kompilacji (tak jak są one w „nietrwałym ABI” Objective-C ”), i / lub na podstawie klasy (tj. każda konkretna klasa ma własną tabelę przesunięć elementów), myślę, że wszystkie 3 cele można osiągnąć.
Croissant Paramagnetic,
@TheParamagneticCroissant: Główny problem semantyczny to nr 1. Jeśli D1i D2oba dziedziczą po Bi każdy przesłaniają funkcję f, a jeśli objjest wystąpieniem typu, Sktóry dziedziczy z obu D1i D2nie przesłania f, to rzutowanie odwołania do Sdo D1powinno dać coś, co fużywa D1przesłonięcia, a rzutowanie na Bnie powinno Zmień to. Podobnie rzutowanie odwołania Sdo D2powinno dać coś, co fużywa D2override, a rzutowanie na Bnie powinno tego zmieniać. Jeśli język nie musiał pozwalać na dodawanie wirtualnych członków ...
supercat
1
@TheParamagneticCroissant: Mogło, a moja lista wyborów była nieco zbyt uproszczona, aby zmieścić się w komentarzu, ale odroczenie czasu działania uniemożliwia autorowi D1, D2 lub S wiedzieć, jakie zmiany mogą wprowadzić bez przerywania konsumentów swojej klasy.
supercat
2

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ę:

public class Abc{

    public void doSomething(){

    }

}

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ć:

public class Abc extends String{

    public void doSomething(){

    }

}

To jest w porządku, ponieważ twoja klasa nie będzie niejawnie rozszerzać, Objectale będzie rozszerzać, Stringponieważ to powiedziałeś. Rozważ następującą zmianę:

public class Abc{

    public void doSomething(){

    }

    @Override
    public String toString(){
        return "hello";
    }

}

Teraz Twoja klasa zawsze będzie zwracać „hello”, jeśli wywołasz toString ().

Teraz wyobraź sobie następującą klasę:

public class Flyer{

    public void makeFly(){

    }

}

public class Bird extends Abc, Flyer{

    public void doAnotherThing(){

    }

}

Ponownie klasa Flyerniejawna rozszerza Object, który ma metodę toString(), każda klasa będzie miała tę metodę, ponieważ wszystkie one rozszerzają się Objectpośrednio, więc jeśli wywołasz toString()from Bird, z której toString()javy musiałby skorzystać? Od Abclub Flyer? 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) .

public interface Flyer{

    public void makeFly(); // <- method without implementation

}

public class Bird extends Abc implements Flyer{

    public void doAnotherThing(){

    }

    @Override
    public void makeFly(){ // <- implementation of Flyer interface

    }

    // Flyer does not have toString() method or any method from class Object, 
    // no method signature collision will happen here

}
Murillo Ferreira
źródło
1

Ponieważ interfejs to tylko umowa. Klasa jest w rzeczywistości pojemnikiem na dane.

Wąż
źródło
Podstawową trudnością związaną z dziedziczeniem wielokrotnym jest możliwość, że klasa może dziedziczyć element członkowski za pośrednictwem wielu ścieżek, które implementują go w inny sposób, bez dostarczania własnej zastępującej implementacji. Dziedziczenie interfejsu pozwala tego uniknąć, ponieważ jedynymi elementami składowymi interfejsu w miejscu, które można zaimplementować, są klasy, których potomkowie będą ograniczone do dziedziczenia pojedynczego.
supercat
1

Na przykład dwie klasy A, B mające tę samą metodę m1 (). A klasa C rozszerza zarówno A, B.

 class C extends A, B // for explaining purpose.

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.

Pooja Khatri
źródło
co powiesz na to, aby kompilator java dał kompilatorowi, jeśli te same metody lub zmienne zdefiniowane w obu klasach nadrzędnych, abyśmy mogli zmienić kod ...
siluveru kiran kumar
1

Java nie obsługuje dziedziczenia wielokrotnego z dwóch powodów:

  1. W Javie każda klasa jest dzieckiem Objectklasy. Gdy dziedziczy z więcej niż jednej superklasy, podklasa uzyskuje niejednoznaczność, aby uzyskać właściwość klasy Object.
  2. W javie każda klasa ma konstruktora, czy piszemy go jawnie, czy nie. Pierwsza instrukcja to wywołanie 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.

raghu.gb Raghu
źródło
0

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.

John Kane
źródło
2
Co by się stało, gdyby ktoś zadzwonił do C.getSomething. Jest to błąd w C ++. Problem rozwiązany.
curiousguy
O to właśnie chodziło ... to był kontrprzykład, który uważałem za jasny. Zwracałem uwagę, że nie ma sposobu, aby określić, którą metodę get coś należy wywołać. Na marginesie, pytanie dotyczyło języka java nie c ++
John Kane,
Przepraszam, nie rozumiem, o co ci chodzi. Oczywiście w niektórych przypadkach zawał serca jest niejednoznaczny. Jak to jest kontrprzykład? Kto twierdził, że MI nigdy nie powoduje niejednoznaczności? „ Na marginesie, pytanie dotyczyło języka java, a nie c ++ ”.
curiousguy
Próbowałem tylko pokazać, dlaczego ta niejednoznaczność istnieje i dlaczego nie ma jej w przypadku interfejsów.
John Kane
Tak, MI może powodować niejednoznaczne połączenia. Więc może przeciążenie. Więc Java powinna usunąć przeciążenie?
curiousguy
0

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.

Nikhil Kumar
źródło
0

Java nie obsługuje dziedziczenia wielokrotnego, wielościeżkowego i hybrydowego z powodu problemu z niejednoznacznością:

 Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so  C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ?  So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.

dziedziczenie wielokrotne

Link referencyjny: https://plus.google.com/u/0/communities/102217496457095083679


źródło
0

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ę.

hasanga lakdinu
źródło
1
Możesz po prostu zamieścić tutaj te przykłady. Bez tego wygląda to jak blok tekstu na pytanie sprzed 9 lat, na które odpowiedziano sto razy.
Pochmurnik
-1

* To prosta odpowiedź, ponieważ jestem początkującym użytkownikiem języka Java *

Rozważmy istnieją trzy klasy X, Ya Z.

Więc dziedziczymy jak X extends Y, Z I oba Yi Zmamy metodę alphabet()z tym samym typem zwracania i argumentami. Ta metoda alphabet()w Ypowiada wyświetlić pierwszy alfabet i metody alfabetu w Zmówi wyświetlania ostatniego alfabetu . Tak więc pojawia się niejednoznaczność, gdy alphabet()jest wywoływany przez X. Czy mówi się o wyświetlaniu pierwszego czy ostatniego alfabetu ??? Dlatego java nie obsługuje dziedziczenia wielokrotnego. W przypadku interfejsów rozważ Yi Zjako 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 klasy X.

Krótko mówiąc, definicja interfejsów jest wykonywana po implementacji, więc nie ma zamieszania.

AJavaLover
źródło