JavaRush /Java Blog /Random-IT /Pausa caffè #95. Come risolvere il problema dell'ereditar...

Pausa caffè #95. Come risolvere il problema dell'ereditarietà multipla in Java

Pubblicato nel gruppo Random-IT
Fonte: FreeCodeCamp Java è uno dei linguaggi di programmazione orientati agli oggetti più popolari utilizzati oggi. Poiché è indipendente dalla piattaforma, puoi trovare applicazioni Java su ogni tipo di dispositivo e ogni sistema operativo. E poiché Java è relativamente facile da imparare, è uno dei linguaggi che molti programmatori padroneggiano. Una caratteristica importante di Java con cui dovresti avere familiarità è l'ereditarietà delle classi. L'ereditarietà ti consente di ottimizzare il codice, semplificando il riutilizzo delle classi. Quando è possibile riutilizzare il codice che è già stato testato e sottoposto a debug, il ciclo di vita dello sviluppo del software diventa più breve e meno costoso. Pausa caffè #95.  Come risolvere il problema dell'ereditarietà multipla in Java - 1Sebbene in teoria sia un concetto semplice, la codifica delle relazioni di ereditarietà richiede attenzione ai dettagli. Soprattutto per quanto riguarda l'ereditarietà multipla, in cui una classe figlia eredita proprietà da più classi genitore. Java rifiuta relazioni di ereditarietà multiple perché creano ambiguità, ma esistono diversi modi per ottenere molti degli stessi effetti se si sa cosa fare. In questo articolo esamineremo i problemi relativi all'ereditarietà multipla e discuteremo le opzioni di codifica alternative in Java.

Terminologia dell'ereditarietà

A volte, per diventare un programmatore di successo, è necessario diventare un risolutore di problemi per trovare soluzioni alternative a errori o problemi comuni. Questa è una parte essenziale della codifica sicura e intelligente. Uno di questi problemi è legato all'ereditarietà multipla (o meglio, alla sua mancanza) in Java. Pausa caffè #95.  Come risolvere il problema dell'ereditarietà multipla in Java - 2Per comprendere appieno l'ereditarietà in Java, è necessario avere familiarità con la terminologia di base dell'ereditarietà della programmazione orientata agli oggetti (OOP).
  • Le classi sono la struttura fondamentale del modello nei linguaggi di programmazione orientati agli oggetti. Una classe definisce proprietà comuni per un gruppo di oggetti.
  • Classe genitore : nota anche come classe base o superclasse. Una classe genitore è una classe estensibile che fornisce funzionalità a una classe figlia. Permette il riutilizzo. Le definizioni e le funzioni della classe genitore vengono riutilizzate durante la creazione delle classi figlie.
  • Classe figlia : chiamata più genericamente sottoclasse, una classe figlia eredita funzionalità da un'altra classe. Le classi figlie sono classi estese o derivate.
  • Ereditarietà : relazione tra classi genitore e figlio.

Tipi di eredità OOP

Oggi sono in uso molti popolari linguaggi di programmazione orientati agli oggetti, tra cui Java, C++, JavaScript, Python, PHP, Ruby e Perl. Sebbene l'ereditarietà sia un concetto comune tra questi linguaggi OOP, non tutti i tipi di ereditarietà esistono in ciascuno di questi linguaggi. È estremamente importante conoscere i tipi comuni di ereditarietà e le restrizioni sull'ereditarietà nella lingua specifica che si sta utilizzando. Quanto più conosci l'ereditarietà, tanto più efficace diventerai uno sviluppatore. I tipi di ereditarietà supportati da Java includono:
  • Ereditarietà a livello singolo : quando una classe figlia eredita funzionalità da una classe genitore singola.
  • Eredità multilivello : questa è una forma multilivello di ereditarietà a livello singolo. Nell'ereditarietà multilivello, una classe figlia può anche fungere da classe genitore per altre classi figlie. La relazione tra ciascun livello è lineare: nessun ramo va più in alto rispetto all'ereditarietà multipla. In questo caso, la classe figlia finale ha funzioni di tutti i livelli superiori.
  • Eredità gerarchica : l'opposto dell'ereditarietà multipla. Nell'ereditarietà gerarchica, una singola classe genitore ha più di una classe figlia. Quindi invece di avere rami sopra, si ramifica sotto.
  • Eredità ibrida : come suggerisce il nome, l'ereditarietà ibrida è una combinazione di altri tipi di ereditarietà.
Oltre ai tipi di ereditarietà sopra elencati, esistono altri tipi che Java non supporta.
  • Ereditarietà multipla : nell'ereditarietà multipla, una classe figlia ha più di una classe genitore. Sebbene Java e JavaScript non supportino l'ereditarietà multipla, i linguaggi OOP come C++ lo fanno.
  • Eredità a percorsi multipli : un ibrido di ereditarietà multipla, multilivello e gerarchica, nell'ereditarietà a percorsi multipli, una classe figlia eredita le sue caratteristiche e funzioni dalla classe genitore e da diverse classi figlie della classe genitore. Poiché l'ereditarietà multipercorso si basa sull'ereditarietà multipla, Java non ne supporta l'utilizzo.

Perché Java non supporta l'ereditarietà multipla

Il problema principale con l'ereditarietà multipla è che può creare ambiguità nelle classi figlie. In un white paper del 1995, il progettista capo di Java James Gosling affermò che i problemi con l'ereditarietà multipla erano uno dei motivi per cui Java fu creato. Le complessità inerenti all’ereditarietà multipla si vedono più chiaramente nel problema dei diamanti. Nel problema del diamante, la classe madre A ha due classi figlie distinte B e C; cioè, le classi figlie B e C estendono la classe A. Pausa caffè #95.  Come risolvere il problema dell'ereditarietà multipla in Java - 3Ora creiamo una nuova classe figlia D che estende sia la classe B che la classe C. Nota che abbiamo ereditarietà multipla (D estende B e C), ereditarietà gerarchica ( B e C estendono A) ed ereditarietà multilivello (D estende A, B e C). Nel problema del diamante, le classi figlie B e C ereditano un metodo dalla classe genitore A. Sia B che C sovrascrivono il metodo ereditato. Ma i nuovi metodi in B e C si contraddicono a vicenda. La classe figlia finale D eredita due metodi indipendenti e contrastanti dai suoi molteplici genitori B e C. Non è chiaro quale metodo della classe D dovrebbe essere utilizzato, quindi sorgono ambiguità. Altri linguaggi di programmazione OOP implementano metodi diversi per risolvere l'ambiguità dell'ereditarietà multipla.

Come risolvere il problema dell'ereditarietà multipla in Java

Solo perché l'ereditarietà multipla è problematica non significa che sia inutile. Esistono molte situazioni in cui potresti volere che una classe abbia funzioni di diverse altre classi. Pensa solo alla Tesla Roadster che comprerai quando diventerai uno sviluppatore di software di grande successo. Le sue caratteristiche tecniche si basano sia sulla classe delle auto sportive che su quella dei veicoli elettrici. Un altro esempio: il browser attraverso il quale stai leggendo questo articolo. Presenta funzionalità della classe delle soluzioni per la privacy su Internet e della classe generale del browser Internet. Ma non puoi estendere più classi in Java. Allora come affronta questo linguaggio il problema dell'ereditarietà multipla? Java utilizza strutture chiamate interfacce. Le interfacce sono tipi astratti che definiscono il comportamento che deve essere implementato dalle classi. Poiché sono astratte, le interfacce non contengono istruzioni dettagliate sul loro comportamento. Invece, le classi forniscono implementazioni concrete del comportamento dell'interfaccia. Le interfacce hanno diverse caratteristiche distintive:
  • A differenza delle classi, non si creano istanze di interfacce. Invece, le classi implementano le interfacce.
  • Le interfacce contengono solo definizioni di costanti pubbliche e intestazioni di metodo.
  • Le interfacce possono solo estendere altre interfacce, non classi.
  • Le interfacce possono estendere più interfacce e le classi possono implementare più interfacce.
Ora possiamo aggirare efficacemente il problema dei diamanti utilizzando le interfacce. Ricordando che solo le interfacce possono estendere solo altre interfacce e che qualsiasi classe che richiede più caratteristiche di ereditarietà deve implementare più interfacce, possiamo sovrascrivere le classi problematiche del diamante. Quelle che erano le classi A, B e C ora diventano interfacce A, B e C. Le interfacce B e C estendono ancora l'interfaccia A, ma nessuna di queste interfacce ha funzionalità specifiche, solo comportamenti specifici. La classe D rimane la classe responsabile dell'implementazione specifica del comportamento riscontrato nelle interfacce B e C. Nota una differenza fondamentale: la classe D non estende le interfacce B e C. Invece, le implementa. In questo modo non hai effettivamente ereditarietà multipla. Invece, hai semplicemente riformulato il problema.

Conclusione

Comprendere l'ereditarietà è essenziale per qualsiasi sviluppatore Java efficace. È altrettanto importante conoscere le limitazioni dell'ereditarietà e la soluzione alternativa integrata in Java per i tradizionali problemi di ereditarietà multipla. Imparare a creare interfacce per ricreare gli effetti dell'ereditarietà multipla in Java migliorerà la tua produttività e le opportunità di assunzione.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION