Aller au contenu principal

Les énumérations (enum)

Notions théoriques

Pourquoi les enum ?

Imaginez que votre jeu RPG définisse les types d'attaque avec des constantes entières :

int PHYSIQUE = 0;
int MAGIQUE = 1;
int SOIN = 2;

Rien n'empêche d'écrire attaquer(42) par erreur. Les enums (énumérations) résolvent ce problème : ils définissent un ensemble fermé et nommé de valeurs valides, vérifiées par le compilateur.

Déclarer un enum

public enum TypeAttaque {
PHYSIQUE,
MAGIQUE,
SOIN
}

Un enum se déclare avec le mot-clé enum. Ses valeurs (les constantes) sont en MAJUSCULES par convention.

Utiliser un enum

TypeAttaque monAttaque = TypeAttaque.PHYSIQUE;

System.out.println(monAttaque.name()); // "PHYSIQUE" (le nom de la constante)
System.out.println(monAttaque.ordinal()); // 0 (l'indice, commence à 0)

Parcourir toutes les valeurs avec values()

for (TypeAttaque type : TypeAttaque.values()) {
System.out.println(type.name() + " → indice " + type.ordinal());
}

Switch expression sur un enum (Java 14+)

Le switch sur un enum est exhaustif : le compilateur vérifie que toutes les valeurs sont traitées. Avec un switch expression, pas besoin de default si tous les cas sont couverts.

TypeAttaque type = TypeAttaque.MAGIQUE;

String description = switch (type) {
case PHYSIQUE -> "Attaque corps à corps";
case MAGIQUE -> "Sort à distance";
case SOIN -> "Restaure des PV";
};

System.out.println(description); // "Sort à distance"
info

Si vous ajoutez une nouvelle valeur à l'enum (ex: POISON) sans mettre à jour le switch, le compilateur générera une erreur. C'est un avantage majeur sur les simples constantes entières.

Enum avec attributs et méthodes

Un enum peut avoir des attributs et des méthodes, comme une classe ordinaire. Le constructeur d'un enum est toujours privé (implicitement ou explicitement).

public enum TypeAttaque {
PHYSIQUE(2), // coefficient 2
MAGIQUE(3), // coefficient 3
SOIN(-1); // coefficient négatif = soin

private final int coefficient;

// Constructeur privé
TypeAttaque(int coefficient) {
this.coefficient = coefficient;
}

public int getCoefficient() {
return coefficient;
}
}

Utilisation :

int degats = force * TypeAttaque.MAGIQUE.getCoefficient(); // force * 3

Utiliser TypeAttaque dans attaquer()

public class Mage extends Personnage {

@Override
public void attaquer(Personnage cible) {
TypeAttaque type = TypeAttaque.MAGIQUE;
int degats = getForce() * type.getCoefficient();
System.out.println(getNom() + " attaque (" + type.name() + "). Dégâts : " + degats);
cible.subirDegats(degats);
}
}

Exemple pratique

// Fichier : TypeAttaque.java
public enum TypeAttaque {
PHYSIQUE(2),
MAGIQUE(3),
SOIN(-1);

private final int coefficient;

TypeAttaque(int coefficient) {
this.coefficient = coefficient;
}

public int getCoefficient() {
return coefficient;
}

public String getDescription() {
return switch (this) {
case PHYSIQUE -> "Attaque physique (coefficient x" + coefficient + ")";
case MAGIQUE -> "Attaque magique (coefficient x" + coefficient + ")";
case SOIN -> "Soin (coefficient x" + Math.abs(coefficient) + " PV restaurés)";
};
}
}

// Fichier : Guerrier.java
public class Guerrier extends Personnage {
private String typeArme;

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

@Override
public void attaquer(Personnage cible) {
TypeAttaque type = TypeAttaque.PHYSIQUE;
int degats = getForce() * type.getCoefficient();
System.out.println(getNom() + " frappe " + cible.getNom() + " (" + typeArme + "). " + type.getDescription());
System.out.println("Dégâts infligés : " + degats);
cible.subirDegats(degats);
}
}

// Fichier : Main.java
public class Main {
public static void main(String[] args) {
// Afficher tous les types d'attaque disponibles
System.out.println("=== Types d'attaque disponibles ===");
for (TypeAttaque type : TypeAttaque.values()) {
System.out.println(type.name() + " (ordinal: " + type.ordinal() + ") → " + type.getDescription());
}

System.out.println();

Guerrier guerrier = new Guerrier("Thor", 120, 18, "épée runique");
Mage mage = new Mage("Elara", 80, 12, 60);

guerrier.attaquer(mage);
mage.attaquer(guerrier);
}
}
info

switch (this) dans la méthode getDescription() d'un enum utilise this pour référencer la constante courante. Le compilateur vérifie que tous les cas (PHYSIQUE, MAGIQUE, SOIN) sont couverts.

Test de mémorisation/compréhension


Quel mot-clé Java permet de déclarer une énumération ?


Quelle méthode retourne toutes les constantes d'un enum sous forme de tableau ?


Quelle méthode retourne le nom (String) d'une constante d'enum ?


Quelle méthode retourne l'indice (position) d'une constante d'enum ?


Par quelle convention sont écrites les constantes d'un enum ?


Quel est le modificateur d'accès implicite du constructeur d'un enum ?


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

Vous allez créer un enum RaretePersonnage avec un coefficient de puissance, et l'utiliser pour modifier les statistiques d'un personnage à sa création.

Étape 1 — Créer l'enum RaretePersonnage

Créez RaretePersonnage.java avec trois niveaux : COMMUN (coefficient 1), RARE (coefficient 2), LEGENDAIRE (coefficient 3).


Bonne pratique - Attributs final dans un enum

Les attributs d'un enum doivent toujours être final. Un enum représente des valeurs immuables : un personnage LEGENDAIRE ne peut pas devenir COMMUN en cours d'exécution. Le mot-clé final l'interdit au niveau du compilateur.

Étape 2 — Utiliser RaretePersonnage dans Personnage

Ajoutez un attribut RaretePersonnage rarete à la classe Personnage et utilisez le coefficient pour multiplier la force à la création.


Bonne pratique - Passer les enums en paramètre

Passer RaretePersonnage rarete en paramètre plutôt qu'un entier int rarete rend le code auto-documenté. new Guerrier("Thor", 120, 18, "épée", RaretePersonnage.LEGENDAIRE) est immédiatement compréhensible, contrairement à new Guerrier("Thor", 120, 18, "épée", 3).

Étape 3 — Afficher la rareté avec un switch expression

Ajoutez une méthode getDescriptionRarete() dans Personnage utilisant un switch expression sur rarete.


Bonne pratique - Switch exhaustif sur les enums

Un switch sur un enum sans default est plus sûr : si vous ajoutez MYTHIQUE à l'enum sans mettre à jour tous les switchs, le compilateur génère une erreur à chaque endroit concerné. Avec un default, l'oubli passe inaperçu.

📌 Une solution