[Review] Voxxed Days Luxembourg 2016 : retour sur Java 8

java8

[Review] Voxxed Days Luxembourg 2016 : retour sur Java 8

java8

 

Duke

Jean-Michel Doudoux, CTO chez Oxiane Luxembourg et champion Java a partagé son retour d’expérience sur Java 8 lors de la conférence Voxxed Days Luxembourg. Il a exposé les nouvelles best practices qu’impliquent cette nouvelle itération mais aussi les pièges qu’elle pose.

 

Java 8 introduit de nombreuses nouvelles fonctionnalités telles que les expressions lambda, les Streams, les parallels arrays et revisite l’API Date & Time. Cette version est probablement à ce jour la plus importante et la plus impactante. Elle suscite un grand intérêt dans la communauté Java même si son adoption est encore faible en entreprise. En effet malgré une salle pleine, peu d’auditeurs avaient déjà eu l’occasion de l’utiliser en milieu professionnel.

 

 

 

 

 

 

 

La classe “Optional

La conférence a débuté sur la nouvelle classe “Optional“. Celle-ci permet d’encapsuler une valeur ou l’absence de valeur et de rendre ainsi le code plus fiable au prix d’un coût très léger sur les performances. Elle suscite de nombreux débats quant à son utilisation :

  • en tant que valeur de retour, elle évite d’avoir à définir une valeur représentant l’absence de valeur, ce qui constitue le cas d’utilisation officiel;
  • en tant que paramètre de méthode, elle pollue la signature d’une méthode et on lui préfère volontier la surcharge de méthode;
  • en tant que variable d’instance, elle doit être employée avec précaution car elle n’est pas sérialisable et peut poser des problèmes de support avec certains framework;
  • en tant que variable locale, on lui préférera l’utilisation de la valeur “null” qui facilement gérable dans le scope d’une méthode.

 

Il faut surtout retenir que :

  • Optional est une classe et qu’elle ne doit jamais être définie comme nulle. Pour l’instancier, il faut passer par des fabriques comme “of()“, “ofNullable()” ou “empty()“.
  • le caractère optionnel d’une valeur doit être limitée
  • pour des valeurs primitives, il convient d’utiliser les classes OptionalInt, OptionalLong, OptionalDouble, …
  • la méthode “get” lève une unchecked exception “NoSuchElementException” si aucune valeur n’est encapsulée. Il convient d’utiliser orElse() pour retourner une valeur par défaut en cas d’absence de valeur.
  • Optional n’apporte aucune valeur ajoutée sur des collections ou des tableaux, la présence d’élément pouvant être testée par isEmpty() ou length().

 

Les parallels arrays

Jean-Michel Doudoux a ensuite abordé les parallels arrays qui permettent d’effectuer en parallèle des traitements sur des tableaux (initialisation, sorting, …). Le traitement en parallèle d’une méthode est invoqué simplement en préfixant la méthode par “parallel”. Si le but de cette feature est d’améliorer les performances de ces opérations, cette optimisation doit se faire de manière réfléchie au risque d’avoir le résultat contraire. C’est par exemple le cas lorsqu’intervient une classe thread-safe comme “Random“. Il est vivement recommandé de tester les performances résultantes de l’utilisation de traitements parallélisés.

 

L’API Date & Time

La gestion du temps a été révisée en profondeur dans Java 8 au travers de l’API Date & Time. Elle remplace au pied levé l’API Date / Calendar et la librairie Joda Time.

 

Une des premières bonnes pratiques à mettre en oeuvre consiste à utiliser le type plutôt qu’une interface pour déclarer une variable. Par exemple, une variable instanciée par LocaleDate.of() doit être assignée à une variable de type “LocalDate” et non “Temporal“.

 

D’autre part, Les classes introduites sont non mutables et permettent de cibler précisément le type de donnée temporelle qu’on souhaite utiliser. En outre, cette API offre la classe Clock qui facilite la mise en place de tests automatisés.

 

Les expressions lambda

Les fonctions lambda offrent à Java la possibilité d’utiliser des “closures“, c’est-à-dire des blocs de code anonymes. Entre autres avantages, elles permettent de se passer facilement des classes internes anonymes. Pour cette feature, le JDK 8 intègre d’un côté un nouveau package java.util.fonction, contenant des interfaces fonctionnelles standards telles que “Function” ou “Predicate” et une nouvelle syntaxe, “::” pour invoquer des blocs de code.

 

Une des premières bonnes pratiques à observer est de définir des expressions lambda les plus simples possibles. Il faut éviter d’inclure des blocs de code et recourir autant que possible aux références de méthode.

 

Une autre contrainte imposée par les expressions lambda concerne la gestion des exceptions : d’une part celles-ci doivent être déclarées dans l’interface fonctionnelle, d’autre part la plupart des interfaces fonctionnelles du JDK ne déclare pas d’exceptions. Ceci implique d’englober le code dans un groupe try / catch pour attraper des exceptions de type Runtime.

 

Les Streams

L’API Stream permet de mettre en place des pipelines de traitements, exécutés de manière séquentielle ou parallèle, sur une séquence d’éléments finie ou non. Son utilisation implique de penser fonctionnel et non impératif, l’application des traitements étant gérée par l’API.

 

Au rang des bonnes pratiques à observer, il faut prêter attention à l’ordre des opérations intermédiaire. En effet celui-ci peut avoir un impact non négligeable sur les performances. A ce titre, il faut noter également que les Streams sont bien adaptés pour les Collections mais moins sur les Maps.

 

Il convient également de ne pas abuser des Streams, une simple boucle étant parfois plus lisible. Enfin, pour rester dans l’esprit de la programmation fonctionnelle, il faut limiter l’utilisation de la méthode forEach().

 

A noter également que l’utilisation de cette API rend le debugging plus difficile : le développeur doit recourir à la méthode “peek” ou utiliser des points d’arrêt sur des références de méthode pour observer le comportement du Stream.

 

Finalement, l’utilisation correcte des Streams implique de détecter certains pièges. Tout d’abord, les Streams infinis doivent impérativement comporter une condition d’arrêt atteignable sans quoi ils boucleront sans fin. Puis tout comme pour les parallels arrays, il faut vérifier que les opérations intermédiaires ne génèrent pas de point de contention au risque de dégrader les performances.

 

Conclusion

Java 8 offre encore beaucoup d’autres fonctionnalités et change profondément la façon de coder certaines fonctionnalités. Mais ces nouveautés doivent être utilisées précautionneusement au risque de dégrader les performances applicatives.

 

L’attrait général pour Java 8 a entraîné son adoption rapide dans des librairies régulièrement utilisées. Par exemple, on peut citer la librairie JUnit 5 qui s’appuie sur les expressions lambdas, les méthodes par défaut et les annotations répétées.

 

Le support de la présentation :

Jérome Recht
No Comments

Post a Comment

Comment
Name
Email
Website