Aller au contenu principal

Le polymorphisme

Notions théoriques

Qu'est-ce que le polymorphisme ?

Le mot polymorphisme vient du grec : "plusieurs formes". En POO, cela signifie qu'une variable de type Personnage peut référencer un objet Guerrier, un objet Mage, ou un objet Archer — et appeler la bonne méthode selon le type réel de l'objet.

Personnage p = new Guerrier("Thor", 120, 18, "épée runique");
p.attaquer(cible); // appelle la méthode attaquer() de Guerrier, pas de Personnage

C'est ce qu'on appelle le dispatch dynamique : Java choisit quelle implémentation de attaquer() appeler au moment de l'exécution, selon le type réel de l'objet (et non le type déclaré de la variable).

Liste hétérogène

Le polymorphisme devient particulièrement puissant avec les listes hétérogènes : une List<Personnage> peut contenir des Guerriers, des Mages, des Archers... et on peut itérer dessus en appelant attaquer() sur chacun.

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

List<Personnage> equipe = new ArrayList<>();
equipe.add(new Guerrier("Thor", 120, 18, "épée"));
equipe.add(new Mage("Elara", 80, 12, 60));
equipe.add(new Archer("Lyra", 90, 14, 20));

Personnage ennemi = new Guerrier("Golem", 200, 8, "poing");

// Chaque personnage attaque à sa manière, sans if/else
for (Personnage p : equipe) {
p.attaquer(ennemi); // dispatch dynamique : Guerrier, Mage ou Archer selon l'objet réel
}

instanceof classique

L'opérateur instanceof permet de tester si un objet est une instance d'un type particulier. Utile avant un cast.

Personnage p = new Guerrier("Thor", 120, 18, "épée");

if (p instanceof Guerrier) {
Guerrier g = (Guerrier) p; // cast explicite
g.coupEpee(ennemi); // méthode spécifique à Guerrier
}

Pattern matching avec instanceof (Java 16+)

Depuis Java 16, une syntaxe plus concise évite le cast manuel :

if (p instanceof Guerrier g) {
g.coupEpee(ennemi); // g est automatiquement typé Guerrier, pas besoin de cast
}

La variable g est disponible uniquement dans le bloc if.

Cast explicite et ClassCastException

Si vous tentez de caster un objet vers un type incompatible, Java lève une ClassCastException à l'exécution.

Personnage p = new Mage("Elara", 80, 12, 60);

Guerrier g = (Guerrier) p; // ERREUR à l'exécution : ClassCastException !
// car p est réellement un Mage, pas un Guerrier
attention

Toujours vérifier avec instanceof avant de caster, sauf si vous êtes certain du type réel. Le pattern matching (Java 16) combine les deux en une seule opération sécurisée.

Exemple pratique

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

// Fichier : Main.java
public class Main {
public static void main(String[] args) {
// Liste hétérogène : tous sont des Personnage, mais de types différents
List<Personnage> equipeHeroes = new ArrayList<>();
equipeHeroes.add(new Guerrier("Thor", 120, 18, "épée runique"));
equipeHeroes.add(new Mage("Elara", 80, 12, 60));
equipeHeroes.add(new Guerrier("Brunhild", 130, 16, "hache de guerre"));

Personnage boss = new Mage("Sauron", 300, 20, 200);

System.out.println("=== Tour d'attaque ===");
for (Personnage p : equipeHeroes) {
if (p.estVivant()) {
p.attaquer(boss); // dispatch dynamique
}
}

System.out.println("\n=== Bilan après le tour ===");
System.out.println(boss.nom + " a " + boss.pointsDeVie + " PV restants.");

// Pattern matching Java 16 : accès aux méthodes spécifiques
System.out.println("\n=== Capacités spéciales ===");
for (Personnage p : equipeHeroes) {
if (p instanceof Mage m) {
System.out.println(m.nom + " a " + m.mana + " mana restant.");
} else if (p instanceof Guerrier g) {
System.out.println(g.nom + " utilise son " + g.typeArme + ".");
}
}
}
}
info

Sans polymorphisme, on devrait écrire un if (p instanceof Guerrier) ... else if (p instanceof Mage) ... pour chaque action. Avec le polymorphisme et la méthode abstraite attaquer(), une seule ligne suffit pour tous les types de personnages.

Test de mémorisation/compréhension


Qu'est-ce que le dispatch dynamique en Java ?


Que permet une List<Personnage> grâce au polymorphisme ?


Que fait l'opérateur instanceof en Java ?


Quelle exception Java est levée si on tente un cast vers un type incompatible ?


Depuis quelle version de Java peut-on utiliser le pattern matching avec instanceof ?


Quelle est la syntaxe du pattern matching instanceof introduite en Java 16 ?


TP pour réfléchir et résoudre des problèmes

Vous allez créer une boucle de combat entre deux équipes en utilisant le polymorphisme pour appeler attaquer() sans savoir quel type de personnage on manipule.

Étape 1 — Créer deux équipes hétérogènes

Dans Main, créez deux listes de Personnage : une équipe de héros et une équipe d'ennemis.


Bonne pratique - Déclarer le type le plus général

Déclarez vos variables avec le type le plus général possible : List<Personnage> plutôt que ArrayList<Personnage>. Cela rend le code plus flexible : vous pourrez changer ArrayList en LinkedList sans modifier le reste du code.

Étape 2 — Boucle de combat par tour

Implémentez une boucle où chaque héros attaque le premier ennemi encore vivant.


Bonne pratique - Vérifier estVivant() avant chaque attaque

Toujours vérifier que la cible est encore vivante avant d'attaquer. Sans cette vérification, un personnage mort continuerait de recevoir des attaques et ses PV deviendraient négatifs, ce qui peut provoquer des comportements inattendus.

Étape 3 — Utiliser le pattern matching pour les capacités spéciales

Après le combat, affichez les mana restants des Mages et les armes des Guerriers en utilisant le pattern matching Java 16.


Bonne pratique - Préférer le pattern matching au cast explicite

Le pattern matching (instanceof Guerrier g) est plus sûr et plus lisible que le cast explicite ((Guerrier) p). Il évite une ClassCastException accidentelle et réduit le nombre de lignes de code.

📌 Une solution