Java 14 : Switch expression
Ce troisième article détaille les évolutions, proposées en standard, dans l’instruction switch
en Java 14.
Cet article fait partie d’une série détaillant les principales fonctionnalités proposées dans Java 14 :
- Java 14 : Vue d’ensemble
- Java 14 : Les records
- Java 14 : Switch expression
- Java 14 : Text Blocks
- Java 14 : Pattern matching avec l’instruction instanceof
Historiquement depuis la première version 1.0 de Java, l’instruction switch
est utilisée comme une structure de contrôle permettant d’exécuter des traitements selon la valeur qui lui est fournie.
Sa syntaxe, inspirée par celle de C/C++, n’est pas exempte de défaut :
- une seule valeur par instruction
case
- fall through par défaut (qui peut être à l’origine de bugs subtils en cas d’oubli d’une instruction
break
) pour permettre d’exécuter un même traitement pour plusieurs valeurs - il n’y a pas de portée spécifique pour les variables définies dans les traitements d’une clause
case
Les instructions switch
sont souvent répétitives (utilisation d’instructions case
et break
) et sujettes à erreurs (oubli d’instructions break
par exemple).
switch (experience) {
case 1 :
System.out.println("Débutant");
break;
case 2 :
System.out.println("Junior");
break;
case 3 :
System.out.println("Confirmé");
break;
case 4 :
System.out.println("Sénior");
break;
case 5 :
System.out.println("Expert");
break;
}
Le but de la JEP 361 est de permettre d’utiliser l’instruction switch
aussi comme une expression et de faire évoluer sa syntaxe.
Des évolutions ont été proposées dans le langage dans deux versions en mode preview :
- La JEP 315 en Java 12 : première preview qui fait évoluer la syntaxe et le rôle de l’instruction
switch
(nouvelle syntaxe avec l’opérateur arrow, utilisation comme une expression, utilisation de multiples valeurs dans les clausescase
, …) - La JEP 354 en Java 13 : une seconde preview qui a introduit l’identifiant restreint
yield
à la place debreak
pour retourner les valeurs
En Java 14, la JEP 361 intègre les évolutions dans l’instruction switch en tant que fonctionnalité standard.
Les types utilisables dans une instruction switch sont toujours les mêmes int
, short
, byte
, char
(et leurs wrappers respectifs), énumération et String
.
La nouvelle syntaxe
Une nouvelle syntaxe simplifie l’utilisation de l’instruction switch
. Elle utilise l’opérateur arrow.
Au-delà du simple remplacement de « :
» par « ->
», plusieurs simplifications sont introduites.
A la droite de l’opérateur ->
, il ne peut y avoir qu’une expression, une instruction unique ou un bloc de code.
Contrairement à la syntaxe historique, si plusieurs instructions doivent être exécutées, il faut les regrouper dans un bloc de code.
Cela est justifié par le fait qu’il n’y a pas de fall-through avec la nouvelle syntaxe.
switch (experience) {
case 1 -> System.out.println("Débutant");
case 2 -> System.out.println("Junior");
case 3 -> System.out.println("Confirmé");
case 4 -> System.out.println("Sénior");
case 5 -> System.out.println("Expert");
}
Cette nouvelle syntaxe peut être utilisée lorsqu’une instruction switch
est utilisée comme structure de contrôle ou comme expression.
Les valeurs multiples dans les clauses case
Il est possible de définir plusieurs valeurs dans une clause case
en les séparant par une virgule.
Avec la nouvelle syntaxe
switch (experience) {
case 1, 2 -> {
System.out.print("Débutant");
System.out.println(" et junior");
}
case 3, 4 -> System.out.println("Confirmé et sénior");
case 5 -> System.out.println("Expert");
}
Mais aussi avec la syntaxe historique
switch (experience) {
case 1, 2:
System.out.print("Débutant");
System.out.println(" et junior");
break;
case 3, 4:
System.out.println("Confirmé et sénior");
break;
case 5:
System.out.println("Expert");
break;
}
L’utilisation de l’instruction switch comme une expression
L’instruction switch
n’est plus uniquement une structure de contrôle permettant d’exécuter du code selon la valeur de la variable passée en paramètre. Elle peut aussi être utilisé comme une expression et donc retourner une valeur.
Avec la nouvelle syntaxe, c’est très simple, il suffit de fournir la valeur retournée à la droite de l’opérateur arrow.
L’utilisation de l’instruction switch
comme une expression implique plusieurs contraintes :
- Toutes les valeurs du type testé doivent être prises en compte : dans la pratique cela implique de toujours utiliser une clause
default
sauf pour une énumération dont toutes les valeurs sont prises en compte dans les cases - Si le switch est la dernière instruction de la ligne, elle doit se terminer par un «
;
»
L’utilisation comme une expression peut se faire avec les deux syntaxes.
Avec la nouvelle syntaxe
String libelle = switch (experience) {
case 1, 2 -> "Débutant et junior";
case 3, 4 -> "Confirmé et sénior";
case 5 -> "Expert";
default -> throw new IllegalArgumentException("valeur experience invalide");
};
System.out.println(libelle);
Mais aussi avec la syntaxe historique : dans ce cas il faut utiliser l’identifiant restreint yield
. yield
est, comme var,
un identifiant restreint : ils peuvent être utilisés comme identifiant mais ont un rôle dans des cas particuliers. Pour yield
, c’est l’utilisation dans les traitements d’un case
.
libelle = switch (experience) {
case 1, 2 : yield "Débutant et junior";
case 3, 4 : yield "Confirmé et sénior";
case 5 : yield "Expert";
default : throw new IllegalArgumentException("valeur experience invalide");
};
System.out.println(libelle);
Note : dans la première preview en Java 12, l’instruction break était utilisée pour retourner une valeur. Suite aux retours de la JEP 325, la seconde preview en Java 13 remplace l’utilisation de l’instruction break
pour retourner une valeur par yield
. Cela évite la confusion avec l’utilisation de break
dans un switch
comme une structure de contrôle.
Cela implique qu’un switch
comme expression utilise yield
pour retourner une valeur, et qu’un switch comme structure de contrôle ne retournant pas de valeur doit utiliser break
dans la syntaxe historique.
yield
doit aussi être utilisé dans un bloc de code avec la nouvelle syntaxe pour préciser la valeur à retourner.
libelle = switch (experience) {
case 1, 2 -> {
System.out.println("cas 1 et 2");
yield "Débutant et junior";
}
case 3, 4 -> "Confirmé et sénior";
case 5 -> "Expert";
default -> throw new IllegalArgumentException("valeur experience invalide");
};
System.out.println(libelle);
Les 4 formes d’utilisation de l’instruction switch
Comme vu précédemment, les deux syntaxes et les deux rôles peuvent se combiner, ce qui donne 4 formes d’utilisation.
Switch comme structure de contrôle avec la syntaxe historique
- C’est la forme historique bien connue
- Fall-throught par défaut
default
facultatif
Switch comme structure de contrôle avec la nouvelle syntaxe
- Chaque case doit avoir une instruction ou un bloc de code
- Pas de fall-through
default
facultatif
Switch comme expression avec la syntaxe historique
- Fall-throught par défaut
- Exhaustivité des cas obligatoire
yield
pour retourner une valeur
Switch comme expression avec la nouvelle syntaxe
- Chaque case doit retourner une valeur ou lever une exception,
yield
pour retourner une valeur dans un bloc de code - Pas de fall-through
- Exhaustivité des cas obligatoire
Il conviendra de tenir compte des contraintes de chacune des formes lors de leur mise en œuvre et de définir des bonnes pratiques d’utilisation avec l’expérience de leurs utilisations.
Les situations illicites
Plusieurs situations seront vérifiées par le compilateur et pourront émettre une erreur :
- Il n’est pas possible de mixer les deux syntaxes dans une même instruction
switch
- Un
switch
utilisé comme expression est une poly expression : si le type de la variable qui reçoit la valeur duswitch
est défini, toutes les valeurs retournées doivent être de ce type, sinon il faut utiliservar
- Un
switch
comme structure de contrôle ne peut pas retourner de valeur
Conclusion
Les évolutions dans la syntaxe et la possibilité d’utiliser un switch
comme une expression, maintenant standard, ouvrent de nouvelles perspectives pour les développeurs.
Les quatre formes d’utilisation de l’instruction switch
requièrent cependant de tenir compte des contraintes inhérentes à chacune des formes.
Les modifications apportées à l’instruction switch
visent aussi à préparer le terrain pour permettre la mise en œuvre du pattern matching dans la JEP 305.
Le prochain article de cette série détaillera une autre évolution dans la syntaxe du langage Java : les blocs de textes.