Pourquoi pas d'héritage multiple de classes
Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy — leçon 26 sur 87.
Java empêche délibérément une class d'étendre plus d'une class. Cette restriction existe en raison d'un problème connu sous le nom de Diamond Problem.
Imaginez si Java autorisait l'héritage multiple. Considérez ce scénario hypothétique :
// Ce code n'est PAS valide 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");
}
}
// PAS autorisé en Java !
public class D extends B, C { }Si vous appeliez greet() sur un objet D, quelle version devrait s'exécuter ? Celle de B ou de C ? Cette ambiguïté est le problème du diamant (Diamond Problem), nommé d'après la forme de diamant formée par le diagramme d'héritage.
La solution de Java est simple : n'autoriser que l'héritage simple de classes. Cependant, Java propose une alternative. Une classe peut implémenter plusieurs interfaces, que vous découvrirez dans un prochain chapitre.
Les interfaces résolvent le problème du diamant différemment et vous offrent de la flexibilité sans l'ambiguïté.
// Ceci EST valide en Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
// Peut implémenter plusieurs interfaces
}Ce choix de conception maintient le modèle d'héritage de Java propre et prévisible, tout en permettant une réutilisation puissante du code via des interfaces.
Défi
FacileConstruisons un système de lecteur de musique qui démontre la règle d'héritage simple de Java et montre comment vous pouvez toujours concevoir des designs flexibles malgré cette contrainte.
Imaginez que vous souhaitiez créer une enceinte connectée qui combine les fonctionnalités d'un lecteur de musique et d'un assistant vocal. Comme Java ne permet pas d'étendre plusieurs classes, vous devrez concevoir votre hiérarchie avec soin en utilisant l'héritage simple.
Vous allez créer quatre fichiers pour organiser votre code :
AudioDevice.java: Créez la classe de base que tous les appareils de lecture audio partagent. Elle doit avoir :- Un champ privé pour
deviceName(String) - Un constructeur qui accepte le nom de l'appareil
- Une méthode
getDeviceName()qui retourne le nom - Une méthode
playSound()qui affiche :[deviceName]: Playing audio
- Un champ privé pour
MusicPlayer.java: Créez une classe qui étend AudioDevice et ajoute des fonctionnalités spécifiques à la musique :- Un champ privé pour
currentSong(String) - Un constructeur qui prend le nom de l'appareil et utilise
super(deviceName) - Une méthode
setSong(String song)qui définit la chanson actuelle - Surchargez
playSound()pour afficher :[deviceName]: Now playing - [currentSong]
- Un champ privé pour
SmartSpeaker.java: Créez une classe qui étend MusicPlayer (et non à la fois MusicPlayer et une classe VoiceAssistant - cela serait de l'héritage multiple !). L'enceinte connectée s'appuie sur le lecteur de musique en ajoutant des fonctionnalités vocales :- Un champ privé pour
assistantName(String) - Un constructeur qui prend le nom de l'appareil et le nom de l'assistant, en utilisant
super(deviceName) - Une méthode
voiceCommand(String command)qui affiche :[assistantName]: Processing "[command]" - Une méthode
getFullInfo()qui affiche :[deviceName] with [assistantName] assistant
- Un champ privé pour
Main.java: Démontrez votre chaîne d'héritage multiniveau. Vous recevrez trois entrées : un nom d'appareil (String), un nom d'assistant (String) et un nom de chanson (String). Créez un SmartSpeaker, définissez une chanson, puis appelez ces méthodes dans l'ordre :getFullInfo()playSound()voiceCommand("skip to next")
Vous recevrez trois entrées : le nom de l'appareil, le nom de l'assistant et le nom de la chanson.
Remarquez comment SmartSpeaker acquiert des capacités musicales en étendant MusicPlayer dans une chaîne (AudioDevice -> MusicPlayer -> SmartSpeaker), plutôt que d'essayer d'étendre plusieurs classes non liées. C'est l'approche de Java pour garder l'héritage propre et sans ambiguïté !
Aide-mémoire
Java n'autorise que l'héritage simple - une classe ne peut étendre qu'une seule autre classe. Cela évite le problème du diamant (Diamond Problem), où une ambiguïté survient si une classe pouvait hériter de la même méthode de plusieurs classes parentes.
// NON autorisé en Java - héritage multiple
public class D extends B, C { } // ERREUR !Cependant, Java permet à une classe d'implémenter plusieurs interfaces comme alternative :
// Valide en Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
// Peut étendre une classe et implémenter plusieurs interfaces
}Java prend en charge l'héritage multiniveau - la création de chaînes d'héritage :
public class AudioDevice { }
public class MusicPlayer extends AudioDevice { }
public class SmartSpeaker extends MusicPlayer { }
// SmartSpeaker hérite à la fois de MusicPlayer et d'AudioDeviceEssayez vous-même
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Lire les entrées
String deviceName = scanner.nextLine();
String assistantName = scanner.nextLine();
String songName = scanner.nextLine();
// TODO: Créer un SmartSpeaker avec deviceName et assistantName
// TODO: Définir la chanson en utilisant setSong()
// TODO: Appeler getFullInfo()
// TODO: Appeler playSound()
// TODO: Appeler voiceCommand("skip to next")
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesChamps (Attributs)Méthode constructeurSurcharge de constructeurRécapitulatif - Calculatrice simple4Héritage
Héritage de base (extends)Le mot-clé superRedéfinition de méthode (@Override)Chaînage de constructeursLa classe ObjectHéritage simple et multiniveauPourquoi pas d'héritage multiple de classesRécapitulatif - Hiérarchie des employés7Méthodes spéciales et classe Object
Méthode toString()equals() et hashCode()Méthode clone()compareTo() et ComparableInterface ComparatorRécapitulatif - Tri personnalisé2Modificateurs d'accès et Encapsulation
Aperçu des niveaux d'accèsMéthodes Getter et SetterMasquage d'informationsLe mot-clé finalRécapitulatif - Gestionnaire de compte bancaire5Polymorphisme
Bases de la surcharge de méthodesRedéfinition de méthodes (Run-Time)Upcasting et DowncastingL'opérateur instanceofClasses et méthodes abstraitesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageAgrégation vs CompositionClasses internes, imbriquées et anonymesEnums et méthodes d'EnumRecords (Java 16+)Classes scellées (Java 17+)11Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron MonteurPatron ObservateurPatron Stratégie3Propriétés de classe et membres statiques
Variables d'instance vs variables statiquesMéthodes statiquesBlocs statiquesConstantes (static final)Récapitulatif - Compteur et utilitaire6Interfaces et Classes Abstraites
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesDefault et Static dans les InterfacesClasses Abstraites vs InterfacesInterfaces FonctionnellesRécapitulatif - Système de Paiement9La généricité
Introduction à la généricitéClasses génériquesMéthodes génériquesParamètres de type bornésWildcards (?, extends, super)Récapitulatif - Conteneur générique12Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositePatron Itérateur