Aller au contenu principal

Les interfaces

Notions théoriques​

Qu'est-ce qu'une interface ?​

Une interface est un contrat que des classes s'engagent à respecter. Elle définit ce qu'une classe doit savoir faire, sans préciser comment elle le fait.

Contrairement à l'héritage (relation "est-un"), une interface modélise une relation "peut faire" ou "est capable de" :

  • Un Mage peut se soigner → Mage implements Soignable
  • Un Guerrier peut attaquer → Guerrier implements Attaquable
// Déclaration d'une interface
public interface Soignable {
void soigner(int points); // méthode abstraite implicitement
}

Déclarer une interface​

Le mot-clé interface remplace class. Toutes les méthodes d'une interface sont publiques et abstraites par défaut : inutile d'écrire public abstract.

public interface Soignable {
void soigner(int points); // implicitement : public abstract void soigner(int points);
}

public interface Attaquable {
void attaquer(Personnage cible);
}

Implémenter une interface avec implements​

Une classe déclare qu'elle respecte le contrat d'une interface avec le mot-clé implements. Elle doit alors fournir un corps à toutes les méthodes de l'interface, avec @Override.

public class Mage extends Personnage implements Soignable {

@Override
public void soigner(int points) {
if (mana >= 10) {
pointsDeVie += points;
mana -= 10;
System.out.println(nom + " se soigne de " + points + " PV !");
} else {
System.out.println(nom + " n'a pas assez de mana pour se soigner.");
}
}
}

Plusieurs interfaces à la fois​

Java n'autorise qu'un seul héritage de classe (extends), mais une classe peut implémenter autant d'interfaces que nécessaire.

public class Paladin extends Personnage implements Soignable, Attaquable {

@Override
public void soigner(int points) { /* ... */ }

@Override
public void attaquer(Personnage cible) { /* ... */ }
}
Une interface peut aussi hériter d'autres interfaces

De la même façon qu'une classe peut implémenter plusieurs interfaces, une interface peut hériter de plusieurs autres interfaces avec extends :

// Combattant regroupe les contrats Soignable et Attaquable
public interface Combattant extends Soignable, Attaquable {
int getNiveau(); // méthode supplémentaire propre à Combattant
}

Toute classe qui implémente Combattant doit alors fournir soigner(), attaquer() et getNiveau().

L'interface comme type​

Une interface peut être utilisée comme type de variable. C'est très utile pour écrire du code générique.

Soignable s = new Mage("Elara", 80, 10, 100);
s.soigner(20); // fonctionne car Mage implements Soignable

// Mais :
s.attaquer(cible); // ERREUR : le type Soignable ne connaît pas attaquer()

Méthodes default (Java 8+)​

Depuis Java 8, une interface peut contenir des méthodes avec un corps grâce au mot-clé default. Cela permet d'ajouter de nouveaux comportements sans casser les classes existantes.

public interface Soignable {
void soigner(int points);

// Méthode default : implémentation fournie, les classes peuvent la redéfinir
default void soignerCompletement() {
soigner(9999);
System.out.println("Soin total !");
}
}

Méthodes static dans une interface​

Une interface peut aussi contenir des méthodes statiques utilitaires.

public interface Soignable {
void soigner(int points);

static int calculerSoin(int niveau) {
return niveau * 10;
}
}

// Appel :
int soin = Soignable.calculerSoin(5); // → 50

Constantes dans une interface​

Les attributs déclarés dans une interface sont implicitement public static final (des constantes).

public interface Attaquable {
int DEGATS_MINIMUM = 1; // implicitement : public static final int DEGATS_MINIMUM = 1;

void attaquer(Personnage cible);
}

Exemple pratique​

// Fichier : Soignable.java
public interface Soignable {
void soigner(int points);

default void soignerCompletement() {
soigner(Integer.MAX_VALUE);
System.out.println("Soin total appliqué !");
}
}

// Fichier : Attaquable.java
public interface Attaquable {
int DEGATS_MINIMUM = 1;
void attaquer(Personnage cible);
}

// Fichier : Mage.java
public class Mage extends Personnage implements Soignable {
int mana;

public Mage(String nom, int pointsDeVie, int force, int mana) {
super(nom, pointsDeVie, force);
this.mana = mana;
}

@Override
public void attaquer(Personnage cible) {
if (mana >= 15) {
int degats = Math.max(force * 3, Attaquable.DEGATS_MINIMUM);
cible.pointsDeVie -= degats;
mana -= 15;
System.out.println(nom + " lance un sort ! Dégâts : " + degats);
} else {
cible.pointsDeVie -= force;
System.out.println(nom + " frappe faiblement.");
}
}

@Override
public void soigner(int points) {
if (mana >= 10) {
pointsDeVie += points;
mana -= 10;
System.out.println(nom + " se soigne de " + points + " PV ! PV actuels : " + pointsDeVie);
} else {
System.out.println(nom + " n'a pas assez de mana pour se soigner.");
}
}
}

// Fichier : Main.java
public class Main {
public static void main(String[] args) {
Mage mage = new Mage("Elara", 80, 12, 50);

// Utiliser le Mage via l'interface Soignable
Soignable soigneur = mage;
soigneur.soigner(30);

// Utiliser la méthode default
soigneur.soignerCompletement(); // méthode default de l'interface
}
}
info

Soignable soigneur = mage; illustre l'utilisation de l'interface comme type. La variable soigneur ne "voit" que les méthodes définies dans Soignable. C'est le polymorphisme par interface.

Test de mémorisation/compréhension​


Quel mot-clé permet à une classe d'implémenter une interface ?


Combien d'interfaces une classe Java peut-elle implémenter ?


Quel mot-clé Java 8 permet d'ajouter une méthode avec un corps dans une interface ?


Dans une interface, les méthodes sans corps sont implicitement :


Peut-on déclarer une variable de type interface ?


Quelle est la nature des attributs déclarés dans une interface ?


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

Vous allez créer deux interfaces Soignable et Attaquable, puis les implémenter dans les classes du RPG.

Étape 1 — Créer l'interface Soignable​

Créez le fichier Soignable.java avec la méthode soigner(int points) et une méthode default afficherPV().


Bonne pratique - Interface à responsabilité unique

Une interface doit représenter un seul comportement cohérent. Préférez plusieurs petites interfaces (Soignable, Attaquable) à une grande interface fourre-tout. C'est le principe de ségrégation des interfaces (Interface Segregation Principle).

Étape 2 — Implémenter Soignable dans Mage​

Modifiez Mage pour implémenter l'interface Soignable. La méthode soigner() coûte 10 de mana et restaure les points de vie.


Bonne pratique - @Override sur les implémentations d'interface

L'annotation @Override s'applique aussi quand on implémente une méthode d'interface, pas seulement quand on redéfinit une méthode héritée. Elle garantit que la signature correspond exactement à celle de l'interface.

Étape 3 — Utiliser l'interface comme type dans Main​

Dans Main, déclarez une variable de type Soignable et appelez la méthode soigner().


Bonne pratique - Coder contre l'interface, pas contre l'implémentation

Écrire Soignable soigneur = new Mage(...) plutôt que Mage soigneur = new Mage(...) est une bonne pratique : le code qui utilise soigneur ne dépend pas de la classe concrète. Si vous remplacez Mage par Pretre (qui implémente aussi Soignable), le reste du code reste inchangé.

📌 Une solution