Aaaah les exceptions ! Une angoisse pour certains, une bénédiction pour les autres, la gestion d’erreur grâce aux exceptions ne laisse personne indifférent.
Allons voir comment Kotlin approche ce processus. :)
Une petite erreur
La gestion d’erreur sur Kotlin est très semblable à celle réalisée sur Java : une fonction peut soit renvoyer un résultat, soit déclencher une exception en cas d’erreur qui sera ensuite gérée (ou non) par le développeur.
private int subtractNumber(int a, int b) throws Exception {
if (a >= b) return a - b;
else throw new Exception("a is smaller than b!")
}
En Java, cette méthode peut lever une exception, et c'est d'ailleurs le cas si le nombre b est plus grand que a.
En Kotlin, nous n'avons pas besoin d'indiquer que la fonction peut déclencher une exception, nous pouvons la lancer directement si besoin :
private fun subtractNumber(a: Int, b: Int) = if (a >= b) a - b else throw Exception("a is smaller than b!")
Dans l’exemple ci-dessus, nous avons créé une fonction appelée subtractNumber()
dont l’objectif est de soustraire deux variables. Si jamais la première variable a
est plus petite que la seconde variable b
, nous renvoyons une exception.
En Java, afin de définir une méthode comme pouvant retourner une exception, nous devions utiliser le mot-clé throws
suivi de la classe de l’exception possiblement renvoyée. Eh bien, en Kotlin, nous n’aurons pas besoin de ce mot-clé ! :)
Bizarre… Pourquoi ? :o
Car Kotlin est un langage de programmation qui se veut moderne. En effet, il ne fera pas la différence entre une "exception vérifiée" et une "exception non vérifiée".
Par exemple, en Java, quand vous utilisez le mot-clé throws
derrière une méthode, vous allez forcer les développeurs à "vérifier" à chaque utilisation, grâce à un try/catch
, que la méthode ne renvoie pas d’exception : l’exception sera alors dite "vérifiée". Cependant, ce genre de pratique peut vite s’avérer très gourmand en lignes de code, et pas forcément très lisible sur des gros projets.
D’ailleurs, Bruce Eckel, auteur du best-seller "Thinking in Java", a lui-même mis en évidence les limitations des exceptions vérifiées en Java :
L'examen de petits programmes a permis de conclure que forcer la gestion des exceptions pouvait améliorer la productivité des développeurs ainsi que la qualité du code. Cependant, l'expérience sur des gros projets logiciels suggère un résultat différent : une baisse de productivité et une faible (voire nulle) amélioration de la qualité du code.
Kotlin s'inspirant de cette réflexion, vous ne serez pas obligé de gérer systématiquement, via un try/catch
dans votre code, une méthode pouvant possiblement renvoyer une exception :
// Exception non gérée
subtractNumber(20,10)
// Exception gérée
try{
subtractNumber(20,10)
} catch(e: Exception) {
print(e.message)
}
Les deux solutions sont ici parfaitement fonctionnelles. La première ne vérifie pas l’exception alors que la deuxième, si. Attention, cependant, à utiliser la première solution dans des situations où vous êtes sûr et certain que la méthode ne pourra pas déclencher l’exception :
subtractNumber(10, 20)
Dans ce premier cas, l'exception non gérée va lancer une erreur et le programme va s'arrêter.
try{
subtractNumber(20,10)
} catch(e: Exception) {
print(e.message)
}
Dans ce deuxième cas, l'exception est gérée et va simplement afficher le message prévu dans ce cas.
You know Nothing
Peut-être l’aurez-vous remarqué : en Kotlin, le try/catch
ainsi que le throw
sont des expressions ! Et cela nous offre par conséquent des possibilités plutôt intéressantes… ;)
Prenons l’exemple suivant :
class User(val email: String?, val password: String?)
val user = User("toto@gmail.com", "azerty")
Nous avons créé une classe User
et l’avons instanciée dans une variable appelée user
. On notera que les propriétés de la classe User
sont toutes susceptibles de contenir une valeur nulle.
So far, so good… :)
Maintenant, jetons un coup d’œil à la ligne de code suivante :
// ?: Opérateur Elvis
val password = user.password ?: throw IllegalArgumentException("Password required")
Tiens ! Encore une expression bizarre... Que peut bien signifier l’opérateur ?:
ici ?
Euuuuuuuuuuh… :'(
Cet opérateur s’appelle "l’opérateur Elvis" et permet de définir une action alternative si l'expression le précédant est égale à nulle. Par exemple, ici, nous faisons en sorte qu’une exception soit levée si le mot de passe de l’utilisateur est égal à nul.
Imaginons maintenant que nous répétions beaucoup de fois cette exception dans notre code. Il serait intéressant de définir une fonction qui serait chargée de déclencher cette exception, non ?
Car oui, en développement, le copier/coller, c’est pour les bébés ! :colere:
Voilà ce que l’on pourrait faire en Kotlin :
// Nothing est le Type retourné d'une expression "throw"
private fun fail(message: String): Nothing = throw IllegalStateException(message)
val password = user.password ?: fail("Password required")
val email = user.email ?: fail("Email required")
Nous avons placé le déclenchement de notre exception dans une fonction à part, fail
, que nous avons appelée ensuite juste après l’opérateur Elvis.
Bizarre ! À quoi correspond la classe Nothing
?
Cette classe correspond tout simplement au type que renvoie une exception. :) Cette fonction ne retournant rien du tout, si ce n’est une exception, son type de retour sera alors Nothing
.
Pour finir, voici un exemple d’utilisation de try/catch
en tant qu’expression, reprenant notre précédente méthode subtractNumber()
:
val result = try { subtractNumber(10,20) } catch (é: Exception) { 0 }
print(result)
Nous avons ici déclaré la variable result
et l’avons initialisée grâce à une expression try/catch
. Si tout se passe bien dans le try
, la variable result
contiendra le résultat de la fonction subtractNumber()
; sinon, cela voudra dire que l’exception a été déclenchée : nous passons donc dans le catch
qui renverra 0
.
Difficile de se lasser des expressions sur Kotlin, n’est-ce pas ? ;)
Practice Makes Perfect!
La théorie, c'est bien, mais la pratique, c'est encore mieux ! Justement, nous vous avons concocté un petit exercice interactif de fin de chapitre pour appliquer toutes ces nouvelles connaissances.
🎓Un petit exercice, ça vous tente ? C'est par ici que ça se passe.
🎁Déjà terminé ? Le corrigé se trouve juste ici.
En résumé
En Kotlin, une exception se gère comme en Java, grâce aux mots-clés
try
etcatch
.Contrairement à Java, en Kotlin, une fonction pouvant renvoyer une exception n’a pas besoin du mot-clé
throws
dans sa signature.try
,catch
etthrow
sont des expressions en Kotlin et pourront donc renvoyer une valeur.La classe
Nothing
représente une "valeur inexistante", utilisée notamment par une fonction ne renvoyant qu’une exception (et jamais de valeur).L’opérateur Elvis
?:
permet de définir une action alternative si l'expression le précédant est égale à nulle.
Pour aller plus loin :
KotlinLang : Exceptions, Elvis Operator