Enum ou constantes
Le problème
Des statuts représentés par des constantes entières :
public static final int STATUT_EN_COURS = 0;
public static final int STATUT_TERMINE = 1;
public static final int STATUT_DEMISSION = 2;
void enregistrer(String nom, int statut) { ... }
enregistrer("Jean Casimir-Perier", STATUT_DEMISSION); // ✅
enregistrer("Jean Casimir-Perier", 42); // 💥 compile sans broncher
enregistrer("Jean Casimir-Perier", MAX_RETRIES); // 💥 compile aussi
Le compilateur voit des int, rien de plus. N'importe quel entier est accepté, les logs affichent 2 au lieu d'un libellé lisible, et ajouter un statut oblige à retrouver à la main chaque if concerné. Une constante est faite pour une valeur fixe ; un choix parmi un ensemble connu relève d'un enum.
Constante ou enum
static final int DUREE_SEPTENNAT = 7; // ✅ une valeur
static final String SEPARATEUR = ";"; // ✅ une valeur
enum Statut { EN_COURS, TERMINE, DEMISSION } // ✅ un choix
Constante static final |
enum |
|
|---|---|---|
| Représente | Une valeur (nombre, texte, seuil) | Un choix parmi un ensemble fermé |
| Typage | Celui de la valeur (int, String) |
Un type dédié |
| Valeur invalide possible | Oui | Non |
| Données et comportement | Non | Oui (champs, méthodes) |
| Affichage | La valeur brute | Le nom de la constante |
Les constantes primitives et String sont intégrées (inlined) dans le bytecode des classes appelantes. Si leur valeur change, les classes qui les utilisent doivent être recompilées.
Un enum est une vraie classe
Chaque constante d'enum est une instance unique, créée au chargement de la classe. Elle peut porter des champs, un constructeur et des méthodes :
public enum Statut {
EN_COURS("Mandat en cours", false),
TERMINE("Mandat terminé", true),
DEMISSION("Démission", true);
private final String libelle;
private final boolean definitif;
Statut(String libelle, boolean definitif) { // constructeur implicitement private
this.libelle = libelle;
this.definitif = definitif;
}
public String libelle() { return libelle; }
public boolean estDefinitif() { return definitif; }
}
Statut s = Statut.DEMISSION;
s.libelle(); // "Démission"
s.estDefinitif(); // true
s == Statut.DEMISSION; // ✅ == est sûr : une seule instance par constante
Comportement par constante
Quand chaque constante a sa propre logique, une méthode abstraite remplace le switch :
public enum Mandat {
SEPTENNAT { int duree() { return 7; } },
QUINQUENNAT { int duree() { return 5; } };
abstract int duree();
}
Un enum peut aussi implémenter une interface, ce qui permet de l'utiliser comme stratégie.
Méthodes fournies
| Méthode | Rôle |
|---|---|
values() |
Tableau de toutes les constantes, dans l'ordre de déclaration |
valueOf("TERMINE") |
Constante à partir de son nom exact, sinon IllegalArgumentException |
name() |
Le nom déclaré, stable |
ordinal() |
La position dans la déclaration (voir plus bas) |
Pour convertir une saisie externe, entoure valueOf ou passe par une recherche explicite :
static Optional<Statut> depuisLibelle(String libelle) {
return Arrays.stream(Statut.values())
.filter(s -> s.libelle().equalsIgnoreCase(libelle))
.findFirst(); // ✅ pas d'exception sur une saisie inconnue
}
EnumMap et EnumSet
Le JDK propose deux collections optimisées pour les clés d'enum. En interne, elles indexent directement par position : un tableau pour EnumMap, un champ de bits pour EnumSet. Elles sont plus rapides et plus compactes que HashMap et HashSet, et itèrent dans l'ordre de déclaration.
Map<Statut, List<String>> parStatut = new EnumMap<>(Statut.class); // ✅
parStatut.computeIfAbsent(Statut.DEMISSION, k -> new ArrayList<>())
.add("Jean Casimir-Perier");
Set<Statut> definitifs = EnumSet.of(Statut.TERMINE, Statut.DEMISSION); // ✅
Set<Statut> tous = EnumSet.allOf(Statut.class);
Set<Statut> autres = EnumSet.complementOf(EnumSet.of(Statut.EN_COURS));
Avec les streams, Collectors.groupingBy accepte une fabrique de map :
Map<Statut, List<President>> groupes = presidents.stream()
.collect(Collectors.groupingBy(President::statut,
() -> new EnumMap<>(Statut.class),
Collectors.toList()));
Ne pas dépendre de ordinal()
ordinal() renvoie la position de la constante dans la déclaration. Cette position change dès qu'on insère, supprime ou réordonne une constante.
enum Statut { EN_COURS, TERMINE, DEMISSION }
// DEMISSION.ordinal() == 2
enum Statut { EN_COURS, INTERIM, TERMINE, DEMISSION }
// DEMISSION.ordinal() == 3 💥 toutes les données stockées avec 2 sont faussées
C'est un piège classique de persistance : avec JPA, @Enumerated stocke par défaut l'ordinal. Préfère le nom, ou mieux, un code explicite et stable :
@Enumerated(EnumType.STRING) // ✅ stocke "DEMISSION"
private Statut statut;
Si un code numérique est imposé (protocole, base existante), déclare-le comme champ de l'enum plutôt que de le déduire de la position.
Ce qu'il ne faut PAS faire
-
❌ Modéliser un choix avec des
intou desString. Aucune vérification de type, aucune valeur interdite. Déclare unenumet type les paramètres avec lui :void enregistrer(String nom, Statut statut) { ... } // ✅ -
❌ Persister ou transmettre
ordinal(). Réordonner les constantes corrompt silencieusement les données. Utilisename(),EnumType.STRINGou un code dédié. -
❌ Appeler
valueOfdirectement sur une saisie utilisateur. Une casse différente ou une faute de frappe lève uneIllegalArgumentException. Passe par une méthode de recherche qui renvoie unOptional. -
❌ Utiliser
HashMap<Statut, …>ouHashSet<Statut>. Ça fonctionne, maisEnumMapetEnumSetsont plus rapides, plus compacts et ordonnés. -
❌ Transformer un enum en fourre-tout mutable. Des champs non
finalmodifiés à l'exécution créent un état global partagé par toute l'application. Garde les champsfinal. -
❌ Remplacer toutes les constantes par des enums. Un seuil, un délai ou un séparateur sont des valeurs, pas des choix : une constante
static finalreste le bon outil.
À retenir
- Constante pour une valeur fixe, enum pour un choix parmi un ensemble fermé.
- Un enum est une classe : champs
final, constructeur, méthodes, comportement par constante. EnumMapetEnumSetsont les collections naturelles pour les clés d'enum.- Ne stocke jamais
ordinal(): utilise le nom ou un code explicite. - Compare les constantes avec
==, convertis les saisies avec une recherche tolérante.