Switch moderne : flèches, valeurs et enums
L'idée en une phrase
Depuis Java 14, le switch s'écrit avec des flèches (->) : plus de break à oublier, il peut renvoyer une valeur, et sur un enum, le compilateur vérifie que tu n'as oublié aucun cas. C'est la forme à utiliser partout.
Une image pour comprendre : l'aiguillage
Un switch, c'est un aiguillage de train : selon la valeur, le train part sur une voie ou sur une autre.
- L'ancien
switcha un défaut : s'il n'y a pas de butoir (lebreak) au bout d'une voie, le train continue sur la voie suivante. - Le nouveau
switchavec->: chaque voie mène à sa propre gare, et le train s'arrête toujours au bon endroit.
Et sur un enum, le compilateur vérifie en plus que chaque valeur possible a sa voie. Aucun train ne peut partir dans le décor.
Le problème de l'ancien switch
String libelle;
switch (statut) {
case EN_COURS:
libelle = "En cours de préparation";
case PAYEE: // ❌ break oublié : EN_COURS continue ici
libelle = "Payée";
break;
case ANNULEE:
libelle = "Annulée";
break;
}
Deux bugs dans ce code, et le compilateur ne dit rien :
- Le
breakoublié : une commande en cours s'affiche « Payée ». - Si quelqu'un ajoute
EXPEDIEEà l'enum, aucun cas ne la traite etlibellen'a pas de valeur.
La forme moderne : switch avec flèches
String libelle = switch (statut) {
case EN_COURS -> "En cours de préparation";
case PAYEE -> "Payée";
case ANNULEE -> "Annulée";
}; // ✅ le point-virgule final est obligatoire : c'est une affectation
Ce qui change :
- pas de
break: chaque flèche ne fait que son cas, impossible de « tomber » dans le suivant ; - le
switchrenvoie une valeur : on l'affecte directement àlibelle; - le compilateur vérifie que tous les cas sont traités.
Le compilateur vérifie l'enum pour toi
C'est le vrai gain. Imagine qu'un collègue ajoute une valeur à l'enum :
enum Statut { EN_COURS, PAYEE, ANNULEE, EXPEDIEE } // nouvelle valeur
Chaque switch qui renvoie une valeur et oublie EXPEDIEE ne compile plus. Le compilateur te montre exactement tous les endroits à mettre à jour, au lieu d'un bug découvert en production.
Plusieurs valeurs pour un même cas
boolean modifiable = switch (statut) {
case EN_COURS -> true;
case PAYEE, ANNULEE, EXPEDIEE -> false; // ✅ plusieurs valeurs séparées par des virgules
};
Quand il n'y a pas de valeur à renvoyer
La flèche s'utilise aussi pour exécuter une action. C'est la forme à préférer, même sans valeur :
switch (statut) {
case PAYEE -> notificationService.confirmerPaiement(commande);
case ANNULEE -> notificationService.confirmerAnnulation(commande);
case EN_COURS, EXPEDIEE -> { } // rien à faire, mais c'est écrit explicitement
}
Note : dans cette forme (sans valeur renvoyée), le compilateur n'oblige pas à traiter tous les cas. Lister quand même tous les cas, même ceux où il n'y a rien à faire, montre au lecteur que tu y as pensé.
Un cas qui demande plusieurs lignes
Entre accolades, le mot-clé yield donne la valeur du cas :
BigDecimal fraisDePort = switch (statut) {
case ANNULEE -> BigDecimal.ZERO;
case EN_COURS, PAYEE, EXPEDIEE -> {
BigDecimal base = new BigDecimal("4.90");
yield commande.montant().compareTo(SEUIL_GRATUIT) >= 0 ? BigDecimal.ZERO : base;
}
};
yield n'est pas un return : il sort du switch, pas de la méthode.
Astuce : dès qu'un bloc dépasse trois ou quatre lignes, extrais-le dans une méthode :
case EN_COURS, PAYEE, EXPEDIEE -> calculerFrais(commande);. Leswitchreste lisible d'un coup d'œil.
Pas de default sur un enum
C'est contre-intuitif, mais important :
String libelle = switch (statut) {
case EN_COURS -> "En cours de préparation";
case PAYEE -> "Payée";
default -> "Autre"; // ❌ EXPEDIEE, ajoutée plus tard, tombera ici sans alerte
};
Le default attrape « tout le reste ». Le compilateur considère donc que tout est traité et ne te prévient plus quand une nouvelle valeur est ajoutée. Tu perds la vérification qui faisait tout l'intérêt de la forme moderne.
Sur un enum, liste toutes les valeurs, sans default.
Le default reste nécessaire quand les valeurs possibles ne sont pas connues à l'avance : une String, un int…
String jour = switch (numeroJour) {
case 6, 7 -> "week-end";
case 1, 2, 3, 4, 5 -> "semaine";
default -> throw new IllegalArgumentException("Jour invalide : " + numeroJour); // ✅
};
Et si la valeur est null ?
Un switch sur une valeur null lance une NullPointerException avant même de regarder les cas. Si le statut peut être absent, vérifie-le avant, ou mieux, fais en sorte qu'il ne puisse pas l'être (un champ obligatoire dans l'entité, une valeur par défaut à la création).
Quand le même switch revient partout
Si tu écris le même switch sur statut dans cinq classes différentes pour obtenir un libellé, l'information a sa place dans l'enum lui-même :
public enum Statut {
EN_COURS("En cours de préparation"),
PAYEE("Payée"),
ANNULEE("Annulée"),
EXPEDIEE("Expédiée");
private final String libelle;
Statut(String libelle) {
this.libelle = libelle;
}
public String libelle() {
return libelle;
}
}
String texte = commande.statut().libelle(); // ✅ plus besoin de switch
Ajouter une valeur à l'enum oblige alors à lui donner un libellé, au même endroit. Le switch reste utile quand le traitement dépend du contexte (une notification ici, un calcul de frais là) plutôt que d'une simple information attachée à la valeur.
Pour aller plus loin : le switch sur un type (Java 21)
Tu croiseras peut-être un switch qui teste le type d'un objet plutôt que sa valeur :
String decrire(Paiement paiement) {
return switch (paiement) {
case PaiementCarte carte -> "Carte se terminant par " + carte.quatreDerniersChiffres();
case PaiementVirement virement -> "Virement de " + virement.banque();
};
}
Chaque cas teste le type et donne directement une variable du bon type, sans conversion. Associé à une interface sealed (qui liste à l'avance tous ses types possibles), le compilateur vérifie, comme pour un enum, qu'aucun type n'est oublié.
C'est une fonctionnalité récente, plutôt utilisée pour modéliser des cas métier avancés. Retiens surtout que la règle est la même : tous les cas listés, pas de default inutile.
Les bonnes habitudes
- Toujours la flèche
->, jamais l'ancienne forme avec:etbreak. - Utilise le
switchcomme une valeur quand il sert à calculer quelque chose :String libelle = switch (...). - Pas de
defaultsur un enum : liste toutes les valeurs, le compilateur te préviendra des oublis. - Des cas courts : au-delà de trois ou quatre lignes, extrais une méthode.
- Le même
switchrépété partout : mets l'information dans l'enum. - Un
defaultqui ne devrait jamais arriver (sur uneString, unint) lance une exception plutôt que de renvoyer une valeur au hasard.
À retenir
- La forme
->supprime lebreaket le risque de « tomber » dans le cas suivant. - Un
switchpeut renvoyer une valeur, et sur un enum, le compilateur vérifie que tous les cas sont traités. - Un
defaultsur un enum désactive cette vérification : à éviter. case A, B ->regroupe plusieurs valeurs ;yielddonne la valeur d'un bloc entre accolades.- Une information répétée dans plusieurs
switcha sa place dans l'enum.