• 12 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 13/11/2023

Maîtrisez les exceptions

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)
Résultat de l'exception non gérée : lancement d'une erreur et arrêt du programme
Résultat de l'exception non gérée : lancement d'une erreur et arrêt du programme

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)
}

Résultat de l'exception gérée : affichage du message prévu dans ce cas
Résultat de l'exception gérée : affichage du message prévu dans ce cas

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  .

Référence : Game of Thrones (bien sûr !)
Référence : Game of Thrones (bien sûr !)

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)
Résultat de
Résultat de "try/catch" en tant qu'expression

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.

En résumé

  • En Kotlin, une exception se gère comme en Java, grâce aux mots-clés  try  et  catch  .

  • Contrairement à Java, en Kotlin, une fonction pouvant renvoyer une exception n’a pas besoin du mot-clé  throws  dans sa signature.

  • try  ,  catch  et  throw  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 :

Exemple de certificat de réussite
Exemple de certificat de réussite