4) Utiliser des interfaces
Interfaces et polymorphisme
Nos guerriers attaquent et nos mages attaquent. Mais certains personnages peuvent aussi soigner leurs alliés. Une interface permet de définir ce contrat de soin, indépendamment de la hiérarchie d'héritage existante.
Objectifs de la séance
- Comprendre la différence entre classe abstraite et interface
- Créer l'interface
Soignableet l'implémenter dansMage - Stocker des objets de types différents dans une même
List<Personnage> - Utiliser
instanceofet le pattern matching Java 16 pour identifier le type réel
Notions théoriques
Qu'est-ce qu'une interface ?
Une interface définit un contrat : elle liste des méthodes que toute classe qui l'implémente doit fournir. Elle ne contient pas d'attributs d'instance ni de constructeur.
public interface Soignable {
void soigner(int points);
}
Depuis Java 8, une interface peut avoir des méthodes default avec un corps. Mais pour les débutants, pensez d'abord aux interfaces comme à des contrats de méthodes abstraites.
implements pour implémenter une interface
public class Mage extends Personnage implements Soignable {
private int mana;
// ... constructeur et attaquer() ...
@Override
public void soigner(int points) {
if (mana >= 5) {
setPointsDeVie(getPointsDeVie() + points);
mana -= 5;
System.out.println(getNom() + " se soigne de " + points + " PV !");
} else {
System.out.println(getNom() + " n'a pas assez de mana pour se soigner.");
}
}
}
Une classe peut hériter d'une seule classe parente (extends) mais peut implémenter plusieurs interfaces (implements A, B, C). C'est la façon dont Java gère l'héritage multiple.
Polymorphisme — liste hétérogène
Le polymorphisme signifie qu'un objet Guerrier peut être référencé par une variable de type Personnage. Cela permet de stocker des guerriers et des mages dans la même liste.
List<Personnage> equipe = new ArrayList<>();
equipe.add(new Guerrier("Aragorn", 150, 30));
equipe.add(new Mage("Gandalf", 100, 20, 50));
for (Personnage p : equipe) {
p.attaquer(autreCible); // appelle la bonne méthode selon le type réel
}
instanceof et pattern matching (Java 16+)
Pour identifier le type réel d'un objet et accéder à ses méthodes spécifiques :
for (Personnage p : equipe) {
// Ancienne syntaxe (avant Java 16)
if (p instanceof Mage) {
Mage m = (Mage) p;
m.soigner(20);
}
// Nouvelle syntaxe — pattern matching (Java 16+)
if (p instanceof Mage m) {
m.soigner(20);
}
}
Le pattern matching p instanceof Mage m teste le type et crée automatiquement la variable m déjà castée. C'est plus concis et élimine le risque de ClassCastException.
Exemple pratique
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Personnage> equipe = new ArrayList<>();
equipe.add(new Guerrier("Aragorn", 150, 30));
equipe.add(new Mage("Gandalf", 80, 20, 50));
equipe.add(new Mage("Saruman", 90, 22, 60));
Personnage ennemi = new Guerrier("Ennemi", 200, 15);
System.out.println("=== Tour d'attaque ===");
for (Personnage p : equipe) {
p.attaquer(ennemi);
}
System.out.println("\n=== Phase de soin ===");
for (Personnage p : equipe) {
if (p instanceof Mage m) {
m.soigner(30);
}
}
System.out.println("\n=== État de l'équipe ===");
for (Personnage p : equipe) {
p.afficher();
}
}
}
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous allez créer l'interface Soignable, l'implémenter dans Mage, puis écrire une boucle de combat avec une liste hétérogène.
Étape 1 — Créer l'interface Soignable
Créez Soignable.java avec la méthode soigner(int points).
Nommez vos interfaces avec un adjectif ou un participe qui décrit la capacité (Soignable, Comparable, Serializable). C'est la convention Java. Cela rend le code lisible : Mage implements Soignable se lit naturellement comme "un Mage est soignable".
Étape 2 — Implémenter soigner() dans Mage
Ajoutez implements Soignable à Mage et implémentez soigner(int points) : si le mage a au moins 5 mana, il augmente ses propres PV et consomme 5 mana.
Toujours vérifier les conditions nécessaires (ici : avoir assez de mana) avant d'effectuer une action. Cela évite des états incohérents (mana négatif, PV abusifs) et rend le comportement du jeu prévisible.
Étape 3 — Boucle de combat avec liste hétérogène
Dans Main, créez une liste List<Personnage> contenant un Guerrier et deux Mage. Faites attaquer tout le monde un ennemi, puis faites soigner les mages.
Préférez if (p instanceof Mage m) à if (p instanceof Mage) { Mage m = (Mage) p; }. La version moderne est plus courte, élimine le cast redondant et est impossible à rater (vous ne pouvez pas oublier de caster).