Perché non esiste l'ereditarietà multipla delle classi
Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 26 di 87.
Java impedisce deliberatamente a una classe di estendere più di un'altra classe. Questa restrizione esiste a causa di un problema noto come problema del diamante.
Immagina se Java consentisse l’ereditarietà multipla. Considera questo scenario ipotetico:
// Este código NO es válido en Java
public class A {
public void greet() {
System.out.println("Hello from A");
}
}
public class B extends A {
@Override
public void greet() {
System.out.println("Hello from B");
}
}
public class C extends A {
@Override
public void greet() {
System.out.println("Hello from C");
}
}
// ¡NO está permitido en Java!
public class D extends B, C { }Se chiamassi greet() su un oggetto D, quale versione dovrebbe essere eseguita? Quella di B o di C? Questa ambiguità è il problema del diamante, chiamato così per la forma a diamante creata dal diagramma di ereditarietà.
La soluzione di Java è semplice: consentire solo l’ereditarietà singola delle classi. Tuttavia, Java offre un’alternativa. Una classe può implementare più interfacce, argomento che approfondirai in un capitolo successivo.
Le interfacce risolvono il problema del diamante in modo diverso e ti offrono flessibilità senza ambiguità.
// Questo È valido in Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
// Può implementare più interfacce
}Questa scelta progettuale mantiene il modello di ereditarietà di Java semplice e prevedibile, consentendo al contempo un potente riutilizzo del codice tramite le interfacce.
Sfida
FacileCostruiamo un sistema di riproduzione musicale che dimostri la regola dell'ereditarietà singola di Java e mostri come puoi comunque ottenere progetti flessibili entro questo vincolo.
Immagina di voler creare uno smart speaker che combini funzionalità sia di un lettore musicale sia di un assistente vocale. Poiché Java non consente di estendere più classi, dovrai progettare attentamente la gerarchia usando l'ereditarietà singola.
Creerai quattro file per organizzare il codice:
AudioDevice.java: crea la classe base condivisa da tutti i dispositivi che riproducono audio. Dovrebbe avere:- Un campo privato per
deviceName(String) - Un costruttore che accetta il nome del dispositivo
- Un metodo
getDeviceName()che restituisce il nome - Un metodo
playSound()che stampa:[deviceName]: Playing audio
- Un campo privato per
MusicPlayer.java: crea una classe che estende AudioDevice e aggiunge funzionalità specifiche per la musica:- Un campo privato per
currentSong(String) - Un costruttore che accetta il nome del dispositivo e usa
super(deviceName) - Un metodo
setSong(String song)che imposta il brano corrente - Sovrascrivi
playSound()per stampare:[deviceName]: Now playing - [currentSong]
- Un campo privato per
SmartSpeaker.java: crea una classe che estende MusicPlayer (non sia MusicPlayer sia una classe VoiceAssistant: sarebbe ereditarietà multipla!). Lo smart speaker si basa sul lettore musicale aggiungendo funzionalità vocali:- Un campo privato per
assistantName(String) - Un costruttore che accetta il nome del dispositivo e il nome dell'assistente, usando
super(deviceName) - Un metodo
voiceCommand(String command)che stampa:[assistantName]: Processing "[command]" - Un metodo
getFullInfo()che stampa:[deviceName] with [assistantName] assistant
- Un campo privato per
Main.java: mostra la tua catena di ereditarietà multilivello. Riceverai tre input: un nome del dispositivo (String), un nome dell'assistente (String) e un nome del brano (String). Crea uno SmartSpeaker, imposta un brano, quindi chiama questi metodi nell'ordine indicato:getFullInfo()playSound()voiceCommand("skip to next")
Riceverai tre input: il nome del dispositivo, il nome dell'assistente e il nome del brano.
Nota come SmartSpeaker acquisisce funzionalità musicali estendendo MusicPlayer in una catena (AudioDevice -> MusicPlayer -> SmartSpeaker), invece di provare a estendere più classi non correlate. Questo è l'approccio di Java per mantenere l'ereditarietà pulita e inequivocabile!
Provalo tu
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leggi gli input
String deviceName = scanner.nextLine();
String assistantName = scanner.nextLine();
String songName = scanner.nextLine();
// TODO: Crea uno SmartSpeaker con deviceName e assistantName
// TODO: Imposta la canzone usando setSong()
// TODO: Chiama getFullInfo()
// TODO: Chiama playSound()
// TODO: Chiama voiceCommand("skip to next")
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione a oggetti
File esterniIntroduzione alla programmazione a oggettiClassi e oggettiLa parola chiave thisMetodiCampi (attributi)Metodo costruttoreSovraccarico dei costruttoriRipasso - Calcolatrice semplice4Ereditarietà
Ereditarietà di base (extends)La parola chiave superSovrascrittura dei metodi (@Override)Concatenamento dei costruttoriLa classe ObjectEreditarietà singola e multilivelloPerché non esiste l'ereditarietà multipla delle classiRiepilogo: gerarchia dei dipendenti7Metodi speciali e classe Object
Metodo toString()equals() e hashCode()Metodo clone()compareTo() e ComparableInterfaccia ComparatorRiepilogo - Ordinamento personalizzato2Modificatori di accesso e incapsulamento
Panoramica dei livelli di accessoMetodi getter e setterNascondimento delle informazioniLa parola chiave finalRiepilogo - Gestore di conti bancari5Polimorfismo
Nozioni di base sull'overloading dei metodiOverride dei metodi (a runtime)Upcasting e downcastingL'operatore instanceofClassi e metodi astrattiRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàAggregazione vs composizioneClassi interne, annidate e anonimeEnum e metodi degli EnumRecord (Java 16+)Classi sealed (Java 17+)11Modelli di progettazione Parte 1
Introduzione ai modelli di progettazionePattern SingletonPattern FactoryPattern BuilderPattern ObserverPattern Strategy3Proprietà di classe e membri statici
Variabili di istanza e staticheMetodi staticiBlocchi staticiCostanti (static final)Riepilogo - Contatore e utility6Interfacce e classi astratte
Introduzione alle interfacceImplementazione delle interfacceImplementazione di più interfacceMetodi default e static nelle interfacceClassi astratte e interfacce a confrontoInterfacce funzionaliRiepilogo: sistema di pagamento9Tipi generici
Introduzione ai tipi genericiClassi genericheMetodi genericiParametri di tipo con limitiCaratteri jolly (?, extends, super)Riepilogo - Contenitore generico12Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern IteratorEsercitati da solo: Compilatore Java online