
Le `when` : une alternative puissante au `switch`
Découvrez la structure de contrôle `when` en Kotlin. Une alternative flexible et puissante au `switch` traditionnel pour gérer multiples conditions, types, et plages.
Introduction : au-delà du `switch` traditionnel
Là où de nombreux langages de programmation (comme Java, C++, C# avant certaines évolutions) proposent une instruction `switch` pour gérer une sélection parmi plusieurs cas basés sur une valeur unique (souvent limitée à des types simples comme les entiers ou les énumérations), Kotlin offre une alternative beaucoup plus flexible et puissante : l'expression `when`.
`when` peut être vu comme une généralisation et une amélioration du `switch`. Il permet non seulement de comparer une valeur à plusieurs constantes, mais aussi de vérifier si elle appartient à une plage ou une collection, de tester son type, ou même d'évaluer des conditions booléennes arbitraires. De plus, tout comme `if`/`else`, `when` peut être utilisé à la fois comme une instruction (pour exécuter du code) et comme une expression (pour retourner une valeur), ce qui le rend extrêmement polyvalent.
Comprendre et maîtriser `when` est essentiel pour écrire du code Kotlin idiomatique, concis et expressif lorsqu'il s'agit de gérer des embranchements conditionnels multiples.
Syntaxe de base : remplacer `if-else if` et `switch`
La forme la plus simple de `when` prend un argument (la valeur à tester) entre parenthèses. Chaque cas possible est défini sur une ligne séparée, avec la valeur (ou la condition) à gauche, suivie d'une flèche `->`, puis du bloc de code ou de l'expression à exécuter/retourner si ce cas correspond.
Syntaxe avec argument :
when (argument) {
valeur1 -> // Code si argument == valeur1
valeur2 -> // Code si argument == valeur2
// ... autres cas ...
else -> // Code si aucun des cas précédents ne correspond (optionnel comme instruction, souvent requis comme expression)
}Dès qu'une correspondance est trouvée, le code associé est exécuté et l'exécution du `when` se termine (il n'y a pas de "fall-through" implicite comme dans certains `switch` où un `break` est nécessaire). La branche `else` capture tous les cas non explicitement listés.
fun describeNumber(x: Int) {
when (x) {
1 -> println("C'est un.")
2 -> println("C'est deux.")
3, 4 -> println("C'est trois ou quatre.") // Plusieurs valeurs pour une branche
in 5..10 -> println("C'est entre cinq et dix (inclus).") // Utilisation d'une plage (range)
!in 11..20 -> println("Ce n'est pas entre onze et vingt.") // Négation d'une plage
else -> {
println("C'est un autre nombre: $x") // Bloc de code pour l'else
println("Traitement par défaut.")
}
}
}
fun main() {
describeNumber(2)
describeNumber(4)
describeNumber(7)
describeNumber(25)
describeNumber(15)
}Ce seul exemple montre déjà la flexibilité de `when` : il gère des valeurs spécifiques, des combinaisons de valeurs (avec la virgule), et même des plages (`in`).
Conditions avancées : plages, collections et types
La puissance de `when` réside dans la variété des conditions que vous pouvez utiliser dans les branches :
- Plusieurs valeurs : Séparez les valeurs par une virgule (`,`) pour exécuter le même code pour plusieurs correspondances exactes (`3, 4 -> ...`).
- Plages (`in` / `!in`) : Utilisez l'opérateur `in` (ou `!in` pour la négation) suivi d'une plage (`start..end`) pour vérifier si la valeur se trouve dans cet intervalle (`in 5..10 -> ...`).
- Collections (`in` / `!in`) : Utilisez `in` (ou `!in`) avec une collection (comme une `List` ou un `Set`) pour vérifier l'appartenance (`in listOf("admin", "moderator") -> ...`).
- Vérification de type (`is` / `!is`) : Utilisez l'opérateur `is` (ou `!is`) pour vérifier si l'argument est d'un certain type. Un avantage majeur ici est que Kotlin effectue un smart cast : si la condition `is Type` est vraie, l'argument est automatiquement traité comme étant de ce `Type` dans la branche correspondante, sans besoin de cast explicite.
fun checkType(obj: Any) { // Any est le supertype de tous les types non-nullables
when (obj) {
is String -> {
println("C'est une chaîne de caractères.")
println("Sa longueur est ${obj.length}") // Smart cast: obj est traité comme String
}
is Int -> {
println("C'est un entier.")
println("Son double est ${obj * 2}") // Smart cast: obj est traité comme Int
}
is List<*> -> { // Vérifie si c'est une liste (peu importe le type des éléments)
println("C'est une liste.")
println("Elle contient ${obj.size} éléments.") // Smart cast: obj est traité comme List
}
!is Boolean -> println("Ce n'est pas un booléen.")
else -> println("Type inconnu ou booléen.")
}
}
fun main() {
checkType("Hello Kotlin")
checkType(42)
checkType(listOf(1, 2, 3))
checkType(true)
checkType(3.14) // Matchera !is Boolean
}`when` sans argument : une chaîne `if-else if` améliorée
Vous pouvez également utiliser `when` sans lui passer d'argument entre parenthèses. Dans ce cas, les conditions dans les branches doivent être des expressions booléennes complètes. `when` évalue chaque branche séquentiellement et exécute le code de la première branche dont la condition est `true`.
Cela le rend très similaire à une chaîne `if-else if-else`, mais souvent avec une syntaxe plus alignée et potentiellement plus lisible.
Syntaxe sans argument :
when {
condition1 -> // Code si condition1 est true
condition2 -> // Code si condition1 est false et condition2 est true
// ... autres conditions ...
else -> // Code si toutes les conditions précédentes sont false
}fun getPermissionLevel(userName: String, userRole: String): String {
return when { // when utilisé comme expression ici
userName == "admin" -> "Super Administrateur"
userRole == "moderator" && userName.startsWith("mod_") -> "Modérateur Confirmé"
userRole == "editor" || userRole == "writer" -> "Contributeur"
userName.isEmpty() -> "Invité (Nom vide)"
else -> "Utilisateur Standard"
}
}
fun main() {
println(getPermissionLevel("admin", "user")) // Super Administrateur
println(getPermissionLevel("mod_alice", "moderator")) // Modérateur Confirmé
println(getPermissionLevel("bob", "writer")) // Contributeur
println(getPermissionLevel("", "user")) // Invité (Nom vide)
println(getPermissionLevel("charlie", "viewer")) // Utilisateur Standard
}`when` utilisé comme une Expression
Tout comme `if`, `when` est plus puissant en Kotlin car il peut être utilisé directement comme une expression. La valeur de l'expression `when` est la valeur de la dernière expression du bloc de la branche qui a été exécutée.
Lorsque `when` est utilisé comme expression (c'est-à-dire que son résultat est assigné à une variable, retourné par une fonction, etc.), le compilateur exige généralement que le `when` soit exhaustif. Cela signifie que toutes les possibilités pour l'argument doivent être couvertes par les branches. Le moyen le plus simple de garantir l'exhaustivité est de fournir une branche `else`.
fun getNumberName(x: Int): String {
val name = when (x) {
1 -> "Un"
2 -> "Deux"
3 -> "Trois"
else -> "Autre"
}
return name
}
// Version plus concise (retourne directement le résultat de when)
fun getNumberNameConcise(x: Int): String {
return when (x) {
1 -> "Un"
2 -> "Deux"
3 -> "Trois"
else -> "Autre" // 'else' est obligatoire ici car x peut être autre chose que 1, 2, 3
}
}
// Exemple avec type checking
fun getTypeCode(obj: Any): Int {
return when (obj) {
is String -> 1
is Int -> 2
is List<*> -> 3
else -> 0 // 'else' est obligatoire car obj peut être d'un autre type
}
}
fun main() {
println(getNumberNameConcise(2)) // Affiche: Deux
println(getNumberNameConcise(5)) // Affiche: Autre
println(getTypeCode("test")) // Affiche: 1
println(getTypeCode(true)) // Affiche: 0
}L'utilisation de `when` comme expression conduit souvent à un code plus fonctionnel et plus lisible, en particulier pour déterminer une valeur basée sur plusieurs conditions.
Exhaustivité et cas particuliers (Enums, Sealed Classes)
Il y a des cas où la branche `else` n'est pas strictement nécessaire pour un `when` utilisé comme expression. C'est lorsque le compilateur peut vérifier statiquement que toutes les possibilités ont été couvertes. Les exemples les plus courants sont lorsque l'argument du `when` est une énumération (`enum`) ou une classe scellée (`sealed class`).
Si vous listez explicitement toutes les constantes de l'énumération ou tous les sous-types de la classe scellée dans les branches du `when`, le compilateur sait qu'il n'y a pas d'autres possibilités, rendant le `else` superflu. C'est une fonctionnalité très utile pour garantir que vous n'oubliez aucun cas lors de l'ajout de nouvelles options à une énumération ou une classe scellée.
enum class Color { RED, GREEN, BLUE }
fun getColorHex(color: Color): String {
// Pas besoin de 'else' car tous les cas de Color sont listés
return when (color) {
Color.RED -> "#FF0000"
Color.GREEN -> "#00FF00"
Color.BLUE -> "#0000FF"
}
}
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result() // Singleton
fun handleResult(result: Result): String {
// Pas besoin de 'else' car tous les sous-types de Result sont gérés
return when (result) {
is Success -> "Données reçues: ${result.data}"
is Error -> "Erreur: ${result.message}"
Loading -> "Chargement en cours..."
}
}
fun main(){
println(getColorHex(Color.GREEN)) // #00FF00
println(handleResult(Success("OK"))) // Données reçues: OK
println(handleResult(Loading)) // Chargement en cours...
}Récapitulatif : la polyvalence de `when`
L'expression `when` est l'un des outils de contrôle de flux les plus puissants et flexibles en Kotlin.
- Elle remplace avantageusement les `switch` traditionnels.
- Peut être utilisée avec ou sans argument.
- Supporte les correspondances multiples (`,`), les plages (`in`), les collections (`in`) et les vérifications de type (`is` avec smart casting).
- Peut être utilisée comme une instruction ou comme une expression (retournant une valeur).
- Garantit généralement l'exhaustivité via la branche `else` lorsqu'elle est utilisée comme expression (sauf pour les enums/sealed classes).
- Ne souffre pas du problème de "fall-through" implicite.
Intégrer `when` dans votre boîte à outils Kotlin vous permettra d'écrire une logique conditionnelle complexe de manière claire, concise et sûre.