Java non consente l'ereditarietà multipla, ma consente l'implementazione di più interfacce. Perché?
Java non consente l'ereditarietà multipla, ma consente l'implementazione di più interfacce. Perché?
Risposte:
Perché le interfacce specificano solo cosa sta facendo la classe, non come la sta facendo.
Il problema con l'ereditarietà multipla è che due classi possono definire modi diversi di fare la stessa cosa e la sottoclasse non può scegliere quale scegliere.
Uno dei miei istruttori del college me lo ha spiegato in questo modo:
Supponiamo che io abbia una classe, che è un tostapane, e un'altra classe, che è NuclearBomb. Entrambi potrebbero avere un'ambientazione "oscura". Entrambi hanno un metodo on (). (Uno ha un off (), l'altro no.) Se voglio creare una classe che è una sottoclasse di entrambi ... come puoi vedere, questo è un problema che potrebbe davvero esplodere in faccia qui .
Quindi uno dei problemi principali è che se hai due classi genitore, potrebbero avere implementazioni diverse della stessa funzione - o forse due caratteristiche diverse con lo stesso nome, come nell'esempio del mio istruttore. Quindi devi occuparti di decidere quale verrà utilizzata dalla tua sottoclasse. Esistono modi per gestirlo, certamente - C ++ lo fa - ma i progettisti di Java ritengono che ciò renderebbe le cose troppo complicate.
Con un'interfaccia, tuttavia, stai descrivendo qualcosa che la classe è in grado di fare, piuttosto che prendere in prestito il metodo di un'altra classe per fare qualcosa. Interfacce multiple hanno molte meno probabilità di causare conflitti delicati che devono essere risolti rispetto a più classi principali.
Poiché l'ereditarietà è abusata anche quando non si può dire "ehi, quel metodo sembra utile, estenderò anche quella classe".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
La risposta a questa domanda sta nel funzionamento interno del compilatore Java (concatenamento del costruttore). Se vediamo il funzionamento interno del compilatore Java:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
Dopo aver compilato questo aspetto come:
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");
}
}
quando estendiamo la classe e ne creiamo un oggetto, una catena di costruttori funzionerà fino alla Object
classe.
Il codice sopra funzionerà bene. ma se abbiamo un'altra classe chiamata Car
che si estende Bank
e una classe ibrida (eredità multipla) chiamata 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");
}
}
In questo caso (SBICar) non riuscirà a creare la catena di costruzione ( compilazione dell'ambiguità temporale ).
Per le interfacce questo è permesso perché non possiamo crearne un oggetto.
Per i nuovi concetti default
e static
metodi, si prega di fare riferimento a default nell'interfaccia .
Spero che questo risolva la tua domanda. Grazie.
L'implementazione di più interfacce è molto utile e non causa molti problemi agli implementatori di linguaggio né ai programmatori. Quindi è permesso. L'eredità multipla, sebbene utile, può causare seri problemi agli utenti (temuto diamante della morte ). E la maggior parte delle cose che fai con l'ereditarietà multipla può essere fatta anche dalla composizione o usando le classi interne. Quindi l'eredità multipla è vietata perché porta più problemi che guadagni.
ToyotaCar
e HybridCar
sia derivato da Car
e sovrascritto Car.Drive
, e se PriusCar
ereditato da entrambi ma non sovrascrittoDrive
, il sistema non avrebbe modo di identificare cosa Car.Drive
dovrebbe fare il virtuale . Le interfacce evitano questo problema evitando la condizione in corsivo sopra.
void UseCar(Car &foo)
; non si può pretendere di includere disambiguazione tra il ToyotaCar::Drive
e HybridCar::Drive
(in quanto dovrebbe spesso né sapere, né la cura che anche gli altri tipi esistono ). Un linguaggio potrebbe, come fa C ++, richiedere quel codice con il ToyotaCar &myCar
desiderio di passarlo a UseCar
deve prima trasmettere a uno HybridCar
o ToyotaCar
, ma poiché ((Auto) (HybridCar) myCar). Guidare` e ((Car)(ToyotaCar)myCar).Drive
farebbe cose diverse, ciò implicherebbe che gli upcasts non preservavano l'identità.
Puoi trovare una risposta precisa a questa domanda nella pagina della documentazione di Oracle relativa all'eredità multipla
Eredità multipla di stato: possibilità di ereditare campi da più classi
Uno dei motivi per cui il linguaggio di programmazione Java non consente di estendere più di una classe è quello di evitare i problemi di eredità multipla di stato, che è la capacità di ereditare campi da più classi
Se l'ereditarietà multipla è consentita e quando si crea un oggetto creando un'istanza di quella classe, quell'oggetto erediterà i campi da tutte le superclassi della classe. Causerà due problemi.
Eredità multipla dell'implementazione: possibilità di ereditare le definizioni dei metodi da più classi
Problemi con questo approccio: denominare conflitti e ambiguità . Se una sottoclasse e una superclasse contengono lo stesso nome di metodo (e firma), il compilatore non può determinare quale versione richiamare.
Ma Java supporta questo tipo di ereditarietà multipla con metodi predefiniti , introdotti dalla versione Java 8. Il compilatore Java fornisce alcune regole per determinare quale metodo predefinito utilizza una determinata classe.
Fare riferimento al seguente post SE per maggiori dettagli sulla risoluzione del problema dei diamanti:
Quali sono le differenze tra classi astratte e interfacce in Java 8?
Eredità multipla di tipo: capacità di una classe di implementare più di un'interfaccia.
Poiché l'interfaccia non contiene campi mutabili, non è necessario preoccuparsi dei problemi derivanti dall'eredità multipla dello stato qui.
Si dice che lo stato degli oggetti sia riferito rispetto ai campi in esso e diventerebbe ambiguo se fossero ereditate troppe classi. Ecco il link
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java supporta l'ereditarietà multipla solo attraverso le interfacce. Una classe può implementare un numero qualsiasi di interfacce ma può estendere solo una classe.
L'ereditarietà multipla non è supportata perché porta a un problema mortale con il diamante. Tuttavia, può essere risolto ma porta a un sistema complesso, quindi l'ereditarietà multipla è stata abbandonata dai fondatori Java.
In un white paper intitolato "Java: an Overview" di James Gosling nel febbraio 1995 ( link ), viene fornita un'idea del perché l'ereditarietà multipla non è supportata in Java.
Secondo Gosling:
"JAVA omette molte funzionalità poco utilizzate, poco comprese e confuse di C ++ che nella nostra esperienza portano più dolore che beneficio. Ciò consiste principalmente nel sovraccarico dell'operatore (sebbene abbia un sovraccarico del metodo), eredità multipla e ampie coercizioni automatiche".
Per lo stesso motivo C # non consente l'ereditarietà multipla ma consente di implementare più interfacce.
La lezione appresa dal C ++ con ereditarietà multipla è stata che ha portato a più problemi di quanti ne valesse la pena.
Un'interfaccia è un contratto di cose che la tua classe deve implementare. Non ottieni alcuna funzionalità dall'interfaccia. L'ereditarietà ti consente di ereditare la funzionalità di una classe genitore (e in ereditarietà multipla, che può diventare estremamente confusa).
Consentire interfacce multiple consente di utilizzare i modelli di progettazione (come l'adattatore) per risolvere gli stessi tipi di problemi che è possibile risolvere utilizzando l'ereditarietà multipla, ma in modo molto più affidabile e prevedibile.
D1
e D2
entrambi ereditano da B
, e ciascuno ha la precedenza su una funzione f
, e if obj
è un'istanza di un tipo S
che eredita da entrambi D1
e D2
ma non ha la precedenza f
, quindi il cast di un riferimento S
a D1
dovrebbe produrre qualcosa di cui f
usa l' D1
override e il cast a B
non dovrebbe cambiarlo. Allo stesso modo, lanciare un riferimento S
a D2
dovrebbe produrre qualcosa che f
usa l' D2
override, e il cast a B
non dovrebbe cambiarlo. Se una lingua non ha bisogno di consentire l'aggiunta di membri virtuali ...
Dato che questo argomento non è vicino, pubblicherò questa risposta, spero che questo aiuti qualcuno a capire perché java non consente l'ereditarietà multipla.
Considera la seguente classe:
public class Abc{
public void doSomething(){
}
}
In questo caso la classe Abc non estende nulla di giusto? Non così in fretta, questa classe implicita estende la classe Object, classe base che consente a tutto di funzionare in Java. Tutto è un oggetto.
Se si tenta di utilizzare la classe di cui sopra vedrete che il vostro IDE consentono di utilizzare metodi come: equals(Object o)
, toString()
, ecc, ma non ha dichiarato i metodi, sono venuti dalla classe baseObject
Puoi provare:
public class Abc extends String{
public void doSomething(){
}
}
Questo va bene, perché la tua classe non si estende implicitamente Object
ma si estende String
perché l'hai detto. Considera la seguente modifica:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Ora la tua classe restituirà sempre "ciao" se chiami toString ().
Ora immagina la seguente classe:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Ancora una volta la classe Flyer
implicita estende l'oggetto che ha il metodo toString()
, qualsiasi classe avrà questo metodo poiché si estende tutti Object
indirettamente, quindi, se chiami toString()
da Bird
, quale toString()
Java dovrebbe usare? Da Abc
o Flyer
? Questo accadrà con qualsiasi classe che tenti di estendere due o più classi, per evitare questo tipo di "collisione del metodo" hanno costruito l'idea di interfaccia , in pratica potresti pensarle come una classe astratta che non estende indirettamente l'oggetto . Poiché sono astratti , dovranno essere implementati da una classe, che è un oggetto (non è possibile installare un'interfaccia da sola, devono essere implementati da una classe), quindi tutto continuerà a funzionare correttamente.
Per differenziare le classi dalle interfacce, la parola chiave implementa era riservata solo alle interfacce.
Potresti implementare qualsiasi interfaccia che ti piace nella stessa classe poiché non estende nulla per impostazione predefinita (ma potresti creare un'interfaccia che estende un'altra interfaccia, ma di nuovo, l'interfaccia "padre" non estende l'oggetto "), quindi un'interfaccia è solo un'interfaccia e non soffriranno di " collisioni della firma dei metodi ", se lo fanno il compilatore ti lancerà un avvertimento e dovrai solo cambiare la firma del metodo per risolverlo (firma = nome metodo + parametri + tipo restituito) .
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
}
Perché un'interfaccia è solo un contratto. E una classe è in realtà un contenitore per i dati.
Ad esempio due classi A, B con lo stesso metodo m1 (). E la classe C estende sia A, B.
class C extends A, B // for explaining purpose.
Ora, la classe C cercherà la definizione di m1. In primo luogo, cercherà in classe se non ha trovato, quindi controllerà in classe genitori. Sia A, B che hanno la definizione Quindi qui si verificano ambiguità quale definizione dovrebbe scegliere. Quindi JAVA NON SUPPORTA L'EREDITÀ MULTIPLA.
Java non supporta l'ereditarietà multipla per due motivi:
Object
classe. Quando eredita da più di una superclasse, la sottoclasse ottiene l'ambiguità di acquisire la proprietà della classe Oggetto.super()
per invocare il costruttore della classe cena. Se la classe ha più di una super classe, viene confusa.Pertanto, quando una classe si estende da più di una superclasse, viene visualizzato l'errore di compilazione.
Prendiamo ad esempio il caso in cui la classe A ha un metodo getSomething e la classe B ha un metodo getSomething e la classe C estende A e B. Cosa accadrebbe se qualcuno chiamasse C.getSomething? Non è possibile determinare quale metodo chiamare.
In sostanza, le interfacce specificano semplicemente quali metodi deve contenere una classe di implementazione. Una classe che implementa più interfacce significa solo che la classe deve implementare i metodi da tutte quelle interfacce. Che non porterebbe a problemi come sopra descritto.
Considera uno scenario in cui Test1, Test2 e Test3 sono tre classi. La classe Test3 eredita le classi Test2 e Test1. Se le classi Test1 e Test2 hanno lo stesso metodo e lo chiami da oggetto di classe figlio, ci sarà ambiguità nel chiamare il metodo di classe Test1 o Test2 ma non c'è tale ambiguità per l'interfaccia come nell'interfaccia non c'è implementazione.
Java non supporta l'ereditarietà multipla, l'ereditarietà multipath e ibrida a causa del problema di ambiguità:
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.
Link di riferimento: https://plus.google.com/u/0/communities/102217496457095083679
in modo semplice lo sappiamo tutti, possiamo ereditare (estendere) una classe ma possiamo implementare così tante interfacce .. questo perché nelle interfacce non diamo un'implementazione, diciamo solo la funzionalità. supponiamo che java possa estendere così tante classi e che abbiano gli stessi metodi .. in questo punto se proviamo a invocare il metodo superclasse nella sottoclasse quale metodo supponiamo di eseguire ??, il compilatore ottiene un esempio confuso : - prova a estendere più ma in interfacce quei metodi non hanno corpi che dovremmo implementare quelli nella sottoclasse .. provare a più strumenti quindi nessuna preoccupazione
* Questa è una risposta semplice poiché sono un principiante in Java *
Considerare ci sono tre classi X
, Y
e Z
.
Quindi stiamo ereditando come X extends Y, Z
E entrambi Y
e stiamo Z
avendo un metodo alphabet()
con lo stesso tipo di ritorno e argomenti. Questo metodo alphabet()
in Y
dice di visualizzare il primo alfabeto e l'alfabeto del metodo in Z
dice visualizzare l'ultimo alfabeto . Quindi ecco che arriva l'ambiguità quando alphabet()
viene chiamato da X
. Indica se visualizzare il primo o l' ultimo alfabeto ??? Quindi java non supporta l'ereditarietà multipla. In caso di interfacce, considerare Y
e Z
come interfacce. Quindi entrambi conterranno la dichiarazione di metodo alphabet()
ma non la definizione. Non dirà se visualizzare il primo alfabeto o l'ultimo alfabeto o altro, ma dichiarerà semplicemente un metodoalphabet()
. Quindi non c'è motivo di aumentare l'ambiguità. Possiamo definire il metodo con tutto ciò che vogliamo all'interno della classe X
.
Quindi, in una parola, la definizione delle interfacce viene eseguita dopo l'implementazione, quindi nessuna confusione.