Perché java non consente l'ereditarietà multipla delle classi quando consente l'ereditarietà multipla delle interfacce? [duplicare]

1

Attualmente mi sto occupando di Jdk 8 e mi sono imbattuto nella funzionalità in cui sono presenti più interfacce che si sovrappongono con la stessa firma del metodo per un metodo predefinito, il compilatore genererà un errore se si tenta di avere una classe implementare entrambe le interfacce, per prevenire il problema dei metodi ambigui con ereditarietà multipla.

La mia domanda è, perché java non può anche consentire l'ereditarietà multipla con classi come fa con le interfacce e risolvere il problema del campo e del metodo ambiguo derivante da più eredità semplicemente lanciando un'eccezione come quando implementa più interfacce che avere firme di metodo predefinite identiche?

    
posta SteelToe 10.01.2017 - 02:13
fonte

4 risposte

6

In parte, questo è storico. Sarebbe molto più difficile aggiungere più eredità ora. Ad esempio, il metodo Class.getSuperclass() non avrebbe più senso, in quanto una classe potrebbe avere più superclassi. Qualsiasi codice che dipende dalla natura di ereditarietà di Java si interromperà.

I campi sono più problematici per l'ereditarietà multipla dei metodi. Considera il caso in cui vuoi ereditare da due superclassi ognuna delle quali ha un campo foobar . Che succede? Proponi che il compilatore rifiuti semplicemente questo. Ma questo significherebbe che esistevano certe coppie di classi che non potevano essere ereditate insieme solo perché gli era capitato di usare lo stesso nome di campo. Questo è diverso dai metodi, perché per i metodi puoi semplicemente sovrascrivere il metodo con il comportamento desiderato.

Naturalmente, potresti creare una semantica per ereditarietà multipla per far funzionare correttamente i campi. Tuttavia, finirebbe per introdurre una discreta quantità di complessità aggiuntiva. Agli occhi dei progettisti java, l'ereditarietà multipla semplicemente non è abbastanza utile da giustificare l'ulteriore complessità.

    
risposta data 10.01.2017 - 06:01
fonte
3

Stai facendo la domanda sbagliata. Non è "perché non può Java", è "perché non Java". Risposta: l'ereditarietà multipla è una seccatura. La maggior parte del codice C ++ non usa ereditarietà multipla. Due linguaggi molto popolari, Objective-C e Swift, non hanno ereditarietà multipla e nessuno si lamenta (e uno di questi è nuovo di zecca, quindi l'ereditarietà multipla non manca per ragioni storiche, ma perché qualcuno ha detto "non vale la pena disturbare "). E in C ++, la maggior parte degli usi attuali provengono da qualcuno che confonde "è un" e "ha un" e sarebbe gestito meglio in un modo diverso.

Ad esempio, implemento un pulsante che visualizza del testo di un certo colore. L'ereditarietà ereditata da pulsante, stringa e colore. Il modo corretto è ereditare dal pulsante e avere due variabili di istanza di tipo stringa e colore.

Le interfacce sono una questione completamente diversa. Le interfacce sono spesso utilizzate per i delegati ed è del tutto normale che un'istanza sia delegata per più classi e quindi per supportare più interfacce. Il supporto di un'interfaccia non eredita nulla, aggiunge allo sviluppatore il requisito di supportare determinati metodi. Più interfacce aggiungono più requisiti per lo sviluppatore.

    
risposta data 10.01.2017 - 11:31
fonte
3

Come menzionato in altre risposte, il motivo principale è il problema dell'eredità del diamante . Prima di Java 8, non c'erano interfacce predefinite quindi questo problema non esisteva in Java e questo era completamente intenzionale.

Una domanda diversa ma correlata potrebbe essere: "se il problema dell'ereditarietà del diamante è così grave, perché è stato introdotto in Java 8?" Per capire che è necessario comprendere perché è stata introdotta la parola chiave default per consentire implementazioni del metodo su interfacce. Il motivo è che una volta rilasciata un'interfaccia, è stato impossibile aggiungervi qualcosa senza rompere le dipendenze esistenti su quell'interfaccia. Aggiungendo un'implementazione predefinita, le interfacce sono più libere di evolvere.

Quindi torniamo alla tua domanda. Ciò che è buono per l'oca è buono per il papero, giusto? Bene, le interfacce sono più simili alle anatre. Il punto di un metodo predefinito non è pensato per essere un posto dove aggiungere implementazioni non banali. Penso che un buon esempio del suo utilizzo sia il metodo remove () su Iterator. In passato ogni volta che volevi implementare un iteratore, dovevi specificare un metodo remove () che (nella mia esperienza) quasi sempre genera un'eccezione di operazione non supportata. L'aggiunta di default non ha lo scopo di modificare il punto delle interfacce. È un'aggiunta pragmatica, non concettuale.

La cosa da tenere a mente con le implementazioni predefinite è che nella specifica per il binding del metodo polimorfico per Java, qualsiasi classe nella gerarchia di ereditarietà ha la precedenza su qualsiasi metodo predefinito anche se tale classe non implementa l'interfaccia che lo ha definito. Se si aggiunge l'ereditarietà multipla delle classi, questa semplice regola che è il modo più diretto per risolvere i conflitti diventa molto più complicata. Non esiste una regola corrispondente che possa essere applicata per le classi.

    
risposta data 10.01.2017 - 19:22
fonte
0

Perché l'eredità multipla non è più popolare? Prendi l'esempio:

  class X 
  {
      public void doSomething() { ... }
  }
  class Y
  {
      public void doSomething() { ... }
  }
  class Z extends X, Y
  {
  }

Quale dovrebbe essere il metodo doSomething della classe Z? Nell'eredità multipla esistono molti di questi conflitti. Sull'ereditarietà singola sarebbe necessario utilizzare la delega. Che è fastidioso ma più controllato.

In quegli esempi è possibile definire che è il doSomething della prima classe estesa. Tuttavia, se immagini X non ha doSomething, sviluppi la tua estensione credendo che Y.doSomethibg sarà chiamato e l'autore della classe X che ha aggiunto il doSomething, quindi una modifica sulla libreria di base introdurrà un'incoerenza sul tuo codice che il compilatore non riconoscere.

Non sto difendendo lo status quo. Penso che le lingue debbano evolversi e incorporare pattern e altri tipi di polimorimam ... Ad esempio, la maggior parte delle volte il pattern builder è un brutto attacco per la mancanza di parametri con nome. Io lo uso su Java, ma non in JavaScript. Ma modifica che richiede tempo e molta ricerca.

    
risposta data 10.01.2017 - 11:52
fonte

Leggi altre domande sui tag