Nouveautés Java 8: Optional is needed? Au revoir les NPE?

java8monadic1-logo

La notion Optional est importante dans java 8 puisque l'API Stream l'utilise comme dans les méthodes min, max, findFirst, findAny, etc.

A titre d'exemple, la méthode max retourne (naturellement) Optional que nous allons utiliser pour illustrer ce billet.

Notons que j'écris cet article sur java8 deux jours après celui que j'ai fait sur Option de scala (espérons que je n'ai pas oublié les ";" et le bruit associé dans les extraits de code java 🙂 ).

La connaissance de scala n'est pas du tout nécessaire pour lire ce billet.

Néanmoins signalons que java 8 s'est inspiré de la notion Option de scala sans trop l'affiner.

Je vais tout d'abord présenter la problématique qui a introduit la notion Optional puis j'enchaînerai par des exemples simples d'utilisation.

Premier exemple

Donnons un premier exemple, le code java 8 suivant fait appel à la méthode max :

N'oubliez pas la ligne d'import des java.util.* et java.util..stream.*

La sortie de ce code est max=11.

Le retour de la méthode max est une Optional<Integer>! Naturel, non? Le max d'une list<T> peut ne pas exister.

Questions

  • Mais que veut dire Optional?
  • Dois-je utiliser l'Optional dans mes projets?
  • Si oui, pour quels bénéfices?

Problématique

Pour mettre en évidence la problématique, reprenons le code précédent de calcul du max d'une liste d'entiers. Et posons nous la question:

Que se passerait-il si la liste entiers contenait un Integer null?

C'est à dire quelle serait la sortie du code ci-dessous.

Seconde exemple

final List<Integer> entiers=Arrays.asList(11,3,null,7);

Optional<Integer> result = entiers.stream().max(Integer::max);

System.out.println("max="+result);

Vous vous en doutez un peu, la sortie est une exception NPE(comme on déteste en voir en production): Exception in thread "main" java.lang.NullPointerException.

Ok, voici donc la problématique!
Je ne peux pas enchaîner les traitements sans tester si l'item est nulle ou non.

Question. Comment contourner cette NPE?

Avant de répondre,  rappelons l'essentiel sur la notion Optional.

Optional is not optional. Définition:

Optional est tout simplement un nouveau type qui permet de dire si la variable contient une valeur ou non ( empty ). On ne va plus parler de null .

Si la variable contient une valeur, alors la méthode get permet d'y accéder. La méthode ifPresent permet de s'assurer de l'existence.

Exemples:

Attention, le code suivant génère une exception:

Que faire pour éviter cette exception?

On peut tout simplement écrire ceci:

Donc la méthode ifPresent s'adapte bien à l'enchaînement des appels (et au functional paradigm)

Sinon la méthode isPresent pourrait être utile sauf que l'on doit pas changer if ..!=null par if( ..isPresent!!!

Résumé

Récapitulons ce que l'on a dit sur Optional:

Soit une variable de type Optional<T>.

Cette variable peut avoir une valeur de type T présente et dans ce cas là, on peut récupérer cette valeur avec la méthode get(), ou la variable peut n'avoir aucune valeur définie (empty) et dans ce cas, toute tentative de la lire avec get déclenche une exception.

Aussi, les méthodes orElse, orElseGet ou bien encore orElseThrow peuvent être utiles pour décider de la démarche à suivre.

Prenons cet exemple,

Réponses aux questions

Nous avons répondu à la question que signifie Optional.

Doit-on utiliser Optional ?
La réponse est oui si on veut épurer le code des NPE.

Et on aura forcément beaucoup moins de (bruit inutile) lignes de code liées à if ( var!=null )... else.

Pour quels bénéfices?
On vient d'en donner un exemple à l'instant. Les enchaînements des traitements deviennent possibles (sans NPE) puisque les éléments vides (Optional.empty) sont tout simplement ignorés.

Exemples.

Reprenons le second exemple avec une liste Integer contenant un élément null.

Comment répondre à la question:"Comment contourner l'exception NPE?"

Le code suivant est un essai de réponse (à cleaner un!):

La première chose à remarquer est qu'à la place de null, on a mis un Optional.empty() dans la liste.

Seconde chose à observer, l'emploi d'un filtre ( a->a.isPresent() ) ne garde dans la liste que les éléments ayant une valeur.

Et pour finir, le max qui est une Optional est affiché s'il existe, sinon rien ne sera affiché et surtout aucune exception levée!

Conclusion:

Revenons à l'avertissement évoqué précédemment, Optional n'est pas sérialisable et la javadoc précise cela:

Ainsi c'est inapproprié de les utiliser dans POJO.

Enfin, j'aurais souhaité terminer par simplifier encore plus le code en utilisant  flatMap qui est approprié avec les listes d'Optional. Mais pour cela, il faudra attendre Java 9 pour pouvoir écrire en toute simplicité le code si simple et si naturel comme celui ci-dessous:

    liste.stream()
          .map(....)
          .flatMap(Optional::stream)
          .max();

Si ce n'est pas tout à fait clair, on aura le temps d'y revenir d'ici l'apparition de Java 9.

Ainsi, on peut le redire, clean code is not Optional!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.