Le problème que résout Optional
Une méthode qui pourrait ne pas avoir de réponse a toujours eu un choix embarrassant en Java : renvoyer null et espérer que l'appelant pense à vérifier. En général, il n'y pense pas, et le résultat est une NullPointerException qui surgit loin de la méthode ayant renvoyé le null.
Optional<T> est une petite boîte qui contient soit une valeur, soit explicitement rien. En renvoyant Optional<User> au lieu de User, une méthode dit à l'appelant « ceci pourrait ne rien trouver » directement dans sa signature - et le compilateur l'oriente vers la gestion de ce cas.
Créer un Optional
Il existe trois méthodes de fabrique, et choisir la bonne compte :
Optional.of(value)- la valeur doit être non nulle, sinon elle lèveNullPointerExceptionsur-le-champ.Optional.ofNullable(value)- vide si la valeur est null, plein sinon. Utilise-la pour envelopper quelque chose qui pourrait être null.Optional.empty()- un optional vide.
Une erreur courante est de recourir à Optional.of(x) sur une valeur qui pourrait être null - cela va à l'encontre du but et lève exactement l'exception que tu essayais d'éviter. En cas de doute, utilise ofNullable.
Lire la valeur en toute sécurité
Une fois que tu as un optional, l'objectif est d'en extraire la valeur sans supposer qu'elle est là. L'outil brutal est get(), et tu ne devrais presque jamais l'utiliser :
Optional<String> name = Optional.empty();
String value = name.get(); // lève NoSuchElementException - une NPE déguisée
À la place, fournis une valeur de repli ou réagis à la présence. orElse renvoie une valeur par défaut quand c'est vide ; ifPresent exécute du code seulement quand une valeur existe :
Utilise orElseGet(supplier) plutôt que orElse quand la valeur par défaut est coûteuse à construire - le supplier ne s'exécute que si l'optional est réellement vide. L'argument d'orElse est toujours évalué, même quand il n'est pas nécessaire.
orElseThrow pour « ceci devrait exister »
Parfois, le vide est vraiment une erreur - une valeur de configuration obligatoire, un utilisateur qui devrait se trouver en base de données. orElseThrow transforme un optional vide en une exception claire de ton choix :
Cela se lit bien mieux qu'un bloc if (opt.isPresent()) et rend l'échec explicite à l'endroit où il se produit.
Transformer avec map et filter
Le vrai intérêt, c'est le chaînage. map applique une fonction à la valeur seulement si elle est présente et laisse un optional vide vide - tu transformes donc sans jamais toucher un null. filter écarte la valeur si elle échoue à un test.
Si ta fonction de mapping renvoie elle-même un Optional, utilise flatMap plutôt que map pour éviter un encombrant Optional<Optional<T>>. Cela reflète la distinction map/flatMap que tu as vue avec les streams - un Optional se comporte beaucoup comme un stream de zéro ou un élément.
Où Optional a sa place (et où non)
Optional est conçu comme un type de retour pour les méthodes qui peuvent légitimement ne produire aucun résultat - les exemples canoniques sont Stream.findFirst(), les recherches de type Map et l'analyse syntaxique. Utilise-le là et ton API documente ses propres absences.
Il n'est pas destiné aux :
- Champs. Il n'est pas sérialisable et ajoute un objet par champ. Utilise des références simples et valide dans le constructeur.
- Paramètres de méthode. L'appelant doit alors envelopper les arguments dans
Optional.of(...), ce qui est plus bruyant qu'une surcharge ou un paramètre acceptant null. - Collections. Renvoie une
Listvide, pas unOptional<List>. Une collection vide signifie déjà « rien ».
Traité comme un type de retour, Optional transforme les bugs de null silencieux en invites à la compilation pour gérer le cas manquant.
Suite : les exceptions
Optional gère proprement le cas quotidien « il pourrait n'y avoir aucune valeur », mais certaines défaillances sont vraiment exceptionnelles - un fichier qui ne s'ouvre pas, un réseau qui lâche, une entrée impossible à analyser. Java modélise ces cas avec des exceptions, et c'est la page suivante.
Questions fréquentes
Qu'est-ce qu'Optional en Java et pourquoi l'utiliser ?
Optional<T> est un conteneur qui contient soit une valeur, soit rien. Il existe pour rendre explicite, dans le type de retour d'une méthode, le fait qu'« il pourrait n'y avoir aucune valeur », afin d'inciter l'appelant à gérer le cas vide plutôt que d'oublier une vérification de null et de tomber sur une NullPointerException à l'exécution. Utilise-le surtout comme type de retour pour des méthodes susceptibles de ne produire aucun résultat, comme une recherche qui ne trouve rien.
Quelle est la différence entre Optional.of et Optional.ofNullable ?
Optional.of(x) lève immédiatement NullPointerException si x est null - utilise-le quand tu sais que la valeur n'est pas null. Optional.ofNullable(x) renvoie un Optional vide quand x est null et un Optional plein sinon - utilise-le quand la valeur pourrait être null. Optional.empty() te donne directement un optional vide.
Dois-je appeler Optional.get() pour lire la valeur ?
Évite le get() brut - il lève NoSuchElementException si l'optional est vide, ce qui n'est qu'une NullPointerException sous un autre nom. Préfère orElse, orElseGet, orElseThrow, ifPresent ou map pour que le cas vide soit toujours géré. Si tu dois vérifier d'abord, protège-toi avec isPresent(), mais les méthodes fonctionnelles sont plus propres.