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 classe d’étendre plus d’une classe. Cette restriction existe en raison d’un problème connu sous le nom de problème du diamant.
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 illustre la règle de l’héritage simple de Java et montre comment créer malgré tout des conceptions flexibles dans cette contrainte.
Imagine que tu souhaites créer une enceinte intelligente qui combine les fonctionnalités d’un lecteur de musique et d’un assistant vocal. Comme Java n’autorise pas l’extension de plusieurs classes, tu devras concevoir soigneusement ta hiérarchie en utilisant l’héritage simple.
Tu vas créer quatre fichiers pour organiser ton code :
AudioDevice.java: crée la classe de base que partagent tous les appareils capables de lire de l’audio. Elle doit posséder :- un champ privé pour
deviceName(String) - un constructeur qui accepte le nom de l’appareil
- une méthode
getDeviceName()qui renvoie le nom - une méthode
playSound()qui affiche :[deviceName]: Playing audio
- un champ privé pour
MusicPlayer.java: crée une classe qui étend AudioDevice et ajoute des fonctionnalités propres à la musique :- un champ privé pour
currentSong(String) - un constructeur qui accepte le nom de l’appareil et utilise
super(deviceName) - une méthode
setSong(String song)qui définit la chanson actuelle - redéfinis
playSound()pour afficher :[deviceName]: Now playing - [currentSong]
- un champ privé pour
SmartSpeaker.java: crée une classe qui étend MusicPlayer (et non à la fois MusicPlayer et une classe VoiceAssistant : cela constituerait un héritage multiple !). L’enceinte intelligente s’appuie sur le lecteur de musique en ajoutant des fonctionnalités vocales :- un champ privé pour
assistantName(String) - un constructeur qui accepte 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: illustre ta chaîne d’héritage multiniveau. Tu recevras trois entrées : un nom d’appareil (String), un nom d’assistant (String) et un nom de chanson (String). Crée une instance de SmartSpeaker, définis une chanson, puis appelle ces méthodes dans l’ordre :getFullInfo()playSound()voiceCommand("skip to next")
Tu recevras trois entrées : le nom de l’appareil, le nom de l’assistant et le nom de la chanson.
Remarque comment SmartSpeaker acquiert des fonctionnalités musicales en étendant MusicPlayer au sein d’une chaîne (AudioDevice -> MusicPlayer -> SmartSpeaker), plutôt qu’en essayant d’étendre plusieurs classes sans lien. C’est l’approche de Java pour conserver un héritage propre et dépourvu d’ambiguïté !
Essayez 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érateurEntraînez-vous par vous-même : Compilateur Java en ligne