CHAPITRE 6.3

Polymorphisme hiérarchique

Utiliser le polymorphisme avec plusieurs classes
Le polymorphisme permet de traiter différents types d'objets de manière uniforme grâce à une référence commune. Cette section montre comment utiliser le polymorphisme dans une hiérarchie de classes.

6.3Polymorphisme dans une hiérarchie d'objets

6.3 – Polymorphisme dans une hiérarchie d'objets

Le polymorphisme permet de traiter différents types d'objets de manière uniforme grâce à une référence commune (la classe mère). Un objet peut être référencé par le type de sa classe mère, ce qui permet un traitement polymorphique où la méthode appropriée est appelée selon le type réel de l'objet.

🔑 Principe du polymorphisme

En Java, une variable de type classe mère peut référencer un objet de n'importe quelle classe fille. À l'exécution, Java détermine automatiquement quelle méthode appeler en fonction du type réel de l'objet (pas du type de la variable).

Exemple : Polymorphisme de base

// Hiérarchie de classes
public class Personne {
    public void sePresenter() {
        System.out.println("Je suis une personne");
    }
}

public class Etudiant extends Personne {
    @Override
    public void sePresenter() {
        System.out.println("Je suis un étudiant");
    }
}

public class Professeur extends Personne {
    @Override
    public void sePresenter() {
        System.out.println("Je suis un professeur");
    }
}

// Utilisation polymorphique
Personne p1 = new Personne();
Personne p2 = new Etudiant();      // Référence de type Personne, objet de type Etudiant
Personne p3 = new Professeur();    // Référence de type Personne, objet de type Professeur

p1.sePresenter();  // Affiche : Je suis une personne
p2.sePresenter();  // Affiche : Je suis un étudiant (polymorphisme)
p3.sePresenter();  // Affiche : Je suis un professeur (polymorphisme)

📦 Polymorphisme avec des tableaux

Le polymorphisme est particulièrement utile avec les collections (tableaux, listes) où vous pouvez stocker différents types d'objets sous une référence commune.

Exemple : Tableau polymorphique

// Créer un tableau de Personne
Personne[] personnes = new Personne[4];

// Stocker différents types d'objets
personnes[0] = new Personne();
personnes[1] = new Etudiant();
personnes[2] = new Professeur();
personnes[3] = new Etudiant();

// Traiter tous les objets de manière uniforme
for (Personne p : personnes) {
    p.sePresenter();  // Appelle la méthode appropriée selon le type réel
}
// Affiche :
// Je suis une personne
// Je suis un étudiant
// Je suis un professeur
// Je suis un étudiant

🔄 Polymorphisme avec des listes

Le polymorphisme fonctionne également avec les collections Java comme les listes.

Exemple : Liste polymorphique

import java.util.ArrayList;
import java.util.List;

List<Personne> personnes = new ArrayList<>();
personnes.add(new Personne());
personnes.add(new Etudiant());
personnes.add(new Professeur());

// Parcourir et traiter polymorphiquement
for (Personne p : personnes) {
    p.sePresenter();
}

⚙️ Liaison dynamique (Dynamic Binding)

Le polymorphisme en Java utilise la liaison dynamique (ou late binding). Cela signifie que la méthode appelée est déterminée à l'exécution en fonction du type réel de l'objet, pas du type de la variable.

Exemple : Liaison dynamique

public class Animal {
    public void faireDuBruit() {
        System.out.println("L'animal fait du bruit");
    }
}

public class Chien extends Animal {
    @Override
    public void faireDuBruit() {
        System.out.println("Wouf wouf !");
    }
}

public class Chat extends Animal {
    @Override
    public void faireDuBruit() {
        System.out.println("Miaou !");
    }
}

// Liaison dynamique
Animal animal1 = new Chien();  // Type de variable : Animal, Type réel : Chien
Animal animal2 = new Chat();   // Type de variable : Animal, Type réel : Chat

animal1.faireDuBruit();  // Appelle la méthode de Chien (type réel)
animal2.faireDuBruit();   // Appelle la méthode de Chat (type réel)

🎯 Avantages du polymorphisme

  • Code réutilisable : Traiter différents types d'objets de manière uniforme
  • Extensibilité : Ajouter de nouvelles classes sans modifier le code existant
  • Maintenabilité : Code plus simple et plus facile à maintenir
  • Flexibilité : Permet de changer l'implémentation sans affecter le code client

📊 Exemple complet : Système de gestion

Exemple : Système de gestion d'employés

public class Employe {
    protected String nom;
    protected double salaire;
    
    public Employe(String nom, double salaire) {
        this.nom = nom;
        this.salaire = salaire;
    }
    
    public void travailler() {
        System.out.println(nom + " travaille");
    }
    
    public double calculerSalaire() {
        return salaire;
    }
}

public class Developpeur extends Employe {
    public Developpeur(String nom, double salaire) {
        super(nom, salaire);
    }
    
    @Override
    public void travailler() {
        System.out.println(nom + " développe du code");
    }
}

public class Manager extends Employe {
    public Manager(String nom, double salaire) {
        super(nom, salaire);
    }
    
    @Override
    public void travailler() {
        System.out.println(nom + " gère une équipe");
    }
    
    @Override
    public double calculerSalaire() {
        return salaire * 1.2;  // Bonus de 20%
    }
}

// Utilisation polymorphique
public class GestionEmployes {
    public static void main(String[] args) {
        Employe[] employes = {
            new Developpeur("Alice", 5000),
            new Manager("Bob", 7000),
            new Developpeur("Charlie", 5500)
        };
        
        // Traiter tous les employés de manière uniforme
        for (Employe e : employes) {
            e.travailler();  // Polymorphisme : méthode appropriée selon le type
            System.out.println("Salaire : " + e.calculerSalaire());
            System.out.println("---");
        }
    }
}

⚠️ Limitations du polymorphisme

Il est important de comprendre que le polymorphisme fonctionne uniquement pour les méthodes. Les attributs ne sont pas polymorphiques.

Important :
  • Le polymorphisme fonctionne pour les méthodes (redéfinies)
  • Les attributs ne sont pas polymorphiques : l'attribut utilisé est celui du type de la variable, pas du type réel
  • Les méthodes static et final ne peuvent pas être redéfinies, donc pas de polymorphisme

Exemple : Attributs non polymorphiques

public class Parent {
    String nom = "Parent";
    
    public void afficher() {
        System.out.println("Nom : " + nom);
    }
}

public class Enfant extends Parent {
    String nom = "Enfant";  // Masque l'attribut de Parent
    
    @Override
    public void afficher() {
        System.out.println("Nom : " + nom);
    }
}

// Utilisation
Parent p = new Enfant();
System.out.println(p.nom);  // Affiche : Parent (attribut non polymorphique)
p.afficher();                // Affiche : Nom : Enfant (méthode polymorphique)

💡 Points clés à retenir

  • Référence commune : Un objet peut être référencé par le type de sa classe mère
  • Liaison dynamique : La méthode appelée est déterminée à l'exécution selon le type réel
  • Collections : Permet de stocker différents types d'objets dans une même collection
  • Extensibilité : Facilite l'ajout de nouvelles classes sans modifier le code existant
  • Méthodes uniquement : Le polymorphisme fonctionne pour les méthodes, pas pour les attributs
Conseils pratiques :
  • Utilisez le polymorphisme pour créer du code générique qui fonctionne avec différentes classes
  • Le polymorphisme est particulièrement utile avec les collections (listes, tableaux)
  • Assurez-vous que les méthodes sont redéfinies (pas seulement héritées) pour bénéficier du polymorphisme
  • Évitez d'accéder directement aux attributs via une référence parente si vous voulez un comportement polymorphique