Expressions switch modernes en Java
Utilisez les expressions switch modernes en Java avec les étiquettes fléchées, yield, l'exhaustivité et le filtrage par motif.
L'instruction switch traditionnelle fait partie de Java depuis la version 1.0, mais elle portait beaucoup de bagages : des bugs de chute entre les cas, des instructions break répétitives, et aucun moyen de produire une valeur. Les expressions switch, finalisées dans Java 14, corrigent tout cela. Elles transforment switch d'une instruction de contrôle de flux maladroite en une expression concise qui produit une valeur.
Ce chapitre parcourt le switch moderne : les étiquettes fléchées, le mot-clé yield, les cas multi-étiquettes, la vérification d'exhaustivité, et exactement comment la nouvelle forme diffère de l'instruction que vous connaissez peut-être déjà.
De l'instruction à l'expression
L'instruction switch classique exécute des effets de bord et repose sur break pour empêcher la chute. Oublier un break et l'exécution tombe silencieusement dans le cas suivant — une source notoire de bugs.
// Traditional switch statement (error-prone)
String kind;
switch (day) {
case SATURDAY:
case SUNDAY:
kind = "weekend";
break; // forget this and you fall through
default:
kind = "weekday";
}Une expression switch réduit cela à une seule affectation. La forme avec flèche (->) ne tombe jamais en chute, donc aucun break n'est nécessaire.
// Modern switch expression
String kind = switch (day) {
case SATURDAY, SUNDAY -> "weekend";
default -> "weekday";
};Le switch entier évalue maintenant vers une valeur, que vous pouvez affecter, retourner ou passer directement en argument.
Étiquettes fléchées et cas multi-étiquettes
L'étiquette fléchée case L -> associe une étiquette (ou plusieurs, séparées par des virgules) à une seule action. Seule la branche correspondante s'exécute — il n'y a pas de chute à craindre.
int numLetters = switch (month) {
case JANUARY, JUNE, JULY -> 4;
case FEBRUARY, MARCH, APRIL, MAY -> 5;
case SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> switchOnLength(month);
case AUGUST -> 6;
};Regrouper les étiquettes avec des virgules remplace l'ancienne astuce qui consistait à empiler des lignes case vides pour partager un corps, et c'est bien plus lisible.
| Fonctionnalité | switch traditionnel (case L:) | switch moderne (case L ->) |
|---|---|---|
| Chute entre les cas | Oui, sauf si vous utilisez break | Non, chaque branche est isolée |
| Produit une valeur | Non | Oui (c'est une expression) |
| Étiquettes multiples | Lignes case vides empilées | Séparées par des virgules sur une ligne |
| Portée des variables | Partagée sur tout le bloc | Locale à chaque bloc de branche |
Blocs et le mot-clé yield
Lorsqu'une branche nécessite plus qu'une simple expression, utilisez un bloc { ... } et retournez sa valeur avec yield. Le mot-clé yield est à une expression switch ce que return est à une méthode : il fournit la valeur que la branche produit.
int gradePoints = switch (grade) {
case 'A' -> 4;
case 'B' -> 3;
default -> {
log("Unknown grade: " + grade);
yield 0; // the value this branch evaluates to
}
};Vous pouvez toujours utiliser des étiquettes avec deux-points et yield si vous préférez l'ancienne syntaxe, mais la forme fléchée est le choix idiomatique moderne et évite entièrement la chute accidentelle.
Exhaustivité et default
Une expression switch doit être exhaustive : chaque entrée possible doit être gérée, car l'expression doit produire une valeur quoi qu'il arrive. Pour la plupart des types, vous satisfaites cette exigence avec une branche default. Pour un enum, le compilateur peut vérifier l'exhaustivité directement — si vous couvrez chaque constante, default devient optionnel.
// No default needed: all enum constants are covered,
// so the compiler knows the switch is exhaustive.
boolean isWeekend = switch (day) {
case SATURDAY, SUNDAY -> true;
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
};Si vous omettez une constante et qu'il n'y a pas de default, le code ne compilera pas. Ce filet de sécurité à la compilation est l'un des plus grands avantages pratiques par rapport à l'ancienne instruction, qui laisserait silencieusement une variable non affectée.
Filtrage par motif dans switch
Les versions récentes de Java étendent switch pour que chaque case puisse correspondre au type de la valeur, et pas seulement à une constante. C'est le filtrage par motif pour switch (en préversion dans Java 17–20, finalisé dans Java 21). Au lieu d'écrire une chaîne de vérifications instanceof et de conversions de type, vous étiquetez chaque branche avec un motif de type et liez une variable en même temps.
static String describe(Object obj) {
return switch (obj) {
case Integer i -> "int " + i; // matches and binds i
case String s -> "string of length " + s.length();
case null -> "nothing"; // null can be its own label
default -> "something else";
};
}Le filtrage par motif est particulièrement puissant avec les records et les types scellés : lorsqu'un switch couvre tous les sous-types autorisés d'un type scellé, le compilateur le traite comme exhaustif, vous pouvez donc supprimer entièrement default.
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static double area(Shape shape) {
return switch (shape) { // no default: all permitted types covered
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
};
}switch ne pouvait tester qu'un enum, un type intégral, ou un String. Les motifs de type permettent à switch de fonctionner sur n'importe quel type référence, ce qui en fait un remplacement naturel pour les longues cascades if/else if.Un exemple complet et fonctionnel
Le programme ci-dessous rassemble les éléments : étiquettes fléchées avec cas multi-étiquettes, un bloc yield pour une branche calculée, couverture exhaustive d'un enum, et une expression switch affectée directement à une variable.
Ce qu'il faut retenir de l'exécution :
kind()retourne directement le résultat d'une expression switch —MONDAYetFRIDAYaffichentweekday,SATURDAYafficheweekend, le tout sans un seulbreak.- Les deux branches fléchées dans
kind()couvrent les sept constantes de l'enum, donc le switch est exhaustif et n'a pas besoin dedefault. - Dans
letterGrade(), les scores 95, 83, 71 et 64 sont mappés proprement via des étiquettes fléchées vers 4, 3, 2 et 1 points de note. - Le score 42 atteint le bloc
default, qui imprime d'abord(failing score 42)via la ligne d'effet de bord puis produit 0 avecyield— montrant comment une branche en bloc peut effectuer du travail avant de produire sa valeur. - Le
switchfinal affecteTWOdirectement dans la variablelabel, prouvant qu'une expression switch est une valeur que vous pouvez stocker, pas seulement un flux de contrôle.
Quand utiliser quelle forme
- Préférez une expression switch (
case L ->) chaque fois que l'objectif est de calculer et retourner une seule valeur. Elle est exhaustive, sans chute, et se lit comme une seule affectation. - Une instruction switch traditionnelle reste appropriée lorsque chaque branche est purement un effet de bord (journalisation, distribution) et qu'il n'y a pas de valeur à produire.
- Utilisez les motifs de type lorsque vous branchez sur le type d'exécution d'un objet, en particulier sur les constantes d'un
enumou les sous-types d'un type scellé.
Pour l'historique complet et la forme avec étiquettes à deux-points, voir les expressions switch Java.