URL: https://linuxfr.org/news/java-14-tombe-le-masque Title: Java 14 tombe le masque Authors: Nonolapéro theojouedubanjo, olivier-maury, Benoît Sibaud et Ysabeau Date: 2020-03-09T23:11:00+01:00 License: CC by-sa Tags: java et openjdk Score: 5 À quelques jours du printemps, une nouvelle version de Java arrive avec son lot de corrections, améliorations, nouveautés et retraits. Voilà six mois que [Java 13](https://linuxfr.org/news/sortie-d-openjdk-13) a apporté ses quelques nouveautés. Cette nouvelle itération est bien plus riche aussi bien sur les fonctionnalités définitives que sur celles en cours de maturation. Au programme pas moins de [16 JEP](https://openjdk.java.net/projects/jdk/14/) intègrent le langage. Seulement une partie sera détaillée dans la suite de dépêche. ---- [Les JEPs OpenJDK 14](https://openjdk.java.net/projects/jdk/14/) [L’ensemble des améliorations et des corrections](https://builds.shipilev.net/backports-monitor/release-notes-14.txt) ---- # Les nouveautés définitives # Avec la nouvelle cadence de versionnement, les nouveautés arrivent plus tôt mais dans un état non définitif et profitent des retours d’expérience des utilisateurs. ## Temps de démarrage et empreinte mémoire ## Depuis plusieurs versions, les temps de démarrage et l’empreinte mémoire diminuent. Il est possible d’espérer un gain d’une dizaine de pourcents sur le temps de démarrage, c’est toujours ça de pris ! Côté empreinte mémoire, par contre, la baisse est plus conséquente, de l’ordre de 30 à 40 %. Ces améliorations sont natives, il n’est pas nécessaire d’ajouter des options. Pour plus de détails, une [présentation](https://linuxfr.org/users/nonolapero/liens/presentation-sur-l-amelioration-des-performances-de-java-depuis-plusieurs-versions) et un [article](https://cl4es.github.io/2019/11/20/OpenJDK-Startup-Update.html). ## Du côté des ramasse-miettes ## ### G1 ### G1 est le ramasse-miettes par défaut depuis Java 8 et il progresse de version en version à tous les niveaux (temps de réponse, gestion de la mémoire). Je vous laisse regarder cette [présentation](https://fosdem.org/2020/schedule/event/g1/) ou le [support](http://cr.openjdk.java.net/~sjohanss/slides/fosdem_2020_-_g1.pdf). Cette progression est une bonne raison pour ne plus rester sur Java 8 car aucune amélioration n’est portée sur les versions antérieures. ### Les ramasse-miettes expérimentaux ### ZGC (disponible depuis Java 11) fonctionne sur les trois principaux systèmes d’exploitation et il devrait bientôt ne plus être expérimental si l’on en croit cette [JEP](https://openjdk.java.net/jeps/8209683). L’idée derrière ZGC est de proposer un ramasse-miettes à faible latence et qui peut aussi bien gérer des petites ou des grosses quantités de mémoire. Shenandoah est un autre ramasse-miettes à faible latence intégré depuis Java 12, son objectif est d’avoir des temps de pause identiques quelle que soit la quantité de mémoire à libérer. Si vous voulez savoir comment ça fonctionne et avoir un état d’avancement du projet, je vous laisse regarder cette [présentation](https://fosdem.org/2020/schedule/event/shenandoah/) et pour les explications plus détaillées c’est [ici (partie 1)](https://developers.redhat.com/blog/2020/03/04/shenandoah-gc-in-jdk-14-part-1-self-fixing-barriers/) puis [là (partie 2)](https://developers.redhat.com/blog/2020/03/09/shenandoah-gc-in-jdk-14-part-2-concurrent-roots-and-class-unloading/). ## Switch statement et switch expression ## Les switch expression et switch statement sont arrivées dans la version 12, ont été revues dans la version 13 et sont définitives dans cette version 14 (voir la [JEP](https://openjdk.java.net/jeps/361)). Avant, on devait écrire : ```java public enum Event { PLAY, PAUSE, STOP } String log; switch (event) { case PLAY: log = "User has triggered the play button"; break; case STOP: case PAUSE: log = "User needs a break"; break; default: String message = event.toString(); LocalDateTime now = LocalDateTime.now(); log = "Unknown event " + message + " logged on " + now; break; } ``` Et maintenant, cette syntaxe est possible : ```java var log = switch (event) { case PLAY -> "User has triggered the play button"; case STOP, PAUSE -> "User needs a break"; default -> { String message = event.toString(); LocalDateTime now = LocalDateTime.now(); yield "Unknown event " + message + " logged on " + now; } }; ``` Avec cette nouvelle forme : * il n’y a plus de risque d’oublier un `break` en cours de chemin ; * il n’est plus nécessaire d’instancier une variable à vide ; * c’est plus court ! Autre fait notable, le cas par défaut n’est plus obligatoire lorsque toutes les possibilités d’une `enum` sont traités. Par précaution, en rajouter une peut protéger s’il y a un ajout dans l’énumération. Le mélange des syntaxes est aussi possible avec des risques d’erreurs comme le montre l’exemple suivant : ```java int i = switch (day) { case MONDAY -> { System.out.println("Monday"); // ERROR! Block doesn't contain a yield statement } default -> 1; }; i = switch (day) { case MONDAY, TUESDAY, WEDNESDAY: yield 0; default: System.out.println("Second half of the week"); // ERROR! Group doesn't contain a yield statement }; ``` Chacun se fera son avis sur ces nouvelles formes car autant un `switch case` à l’ancienne est pénible à écrire, autant avec les nouvelles possibilités il va falloir s’accorder sur le style à suivre sur un projet. ## JDK Flight Recorder Event Streaming ## Dans Java 11, Oracle a libéré [Flight Recorder](https://openjdk.java.net/jeps/328) (FR) qui permet de suivre l’activité d’une application du sol au plafond, c’est-à-dire des appels processeurs aux méthodes utilisées pour faire de l’affichage, en passant par le ramasse-miettes et par le compilateur à la volée. Pour diverses explications, du code et des démos, allez voir cette [conférence](https://www.infoq.com/presentations/monitoring-jdk-jfr/) ou encore [cet article](https://blogs.oracle.com/javamagazine/java-flight-recorder-and-jfr-event-streaming-in-java-14). Toutefois pour découvrir l’outil rapidement, il faut lancer votre application avec `java -XX:StartFlightRecording:filename=/tmp/monAppli.jfr -jar monAppli.jar`, interagir avec quelques instants et l’arrêter. Il est aussi possible de démarrer les enregistrements sur une application déjà en fonctionnement avec `jcmd MainClass JFR.start`. Ensuite, vous pouvez profiter d’un résumé des évènements enregistrés avec `$JAVA_HOME/bin/jfr summary /tmp/monAppli.jfr | less` ; pour une analyse plus poussée, [Java Mission Control](https://adoptopenjdk.net/jmc.html) est bien plus adapté. Le JDK Flight Recorder Event Streaming apporte la possibilité de profiter directement de ces mesures sans passer par la case écriture dans un fichier et lecture à partir de ce dernier. Ça peut sembler basique mais ça ouvre de nombreuses possibilités pour monitorer de façon déportée différents services (un [exemple](https://github.com/gunnarmorling/jfr-custom-events) avec du Grafana et du Quarkus). ## Des NullPointerException plus explicites ## Dorénavant, les développeurs Java auront moins d’excuses pour passer de longs moments à décortiquer les NPE car elles indiqueront quel objet est `null`. Cette fonctionnalité existe depuis 2006 dans le JDK de SAP et la plus grande ouverture des contributions à Java a permis de la rendre disponible au plus grand nombre. Pour Java 14 il faudra activer l’option `-XX:+ShowCodeDetailsInExceptionMessages` afin d’en profiter et, à partir de Java 15, l’option sera activée par défaut. # Nouveautés en prévisualisation # ## Records ## La verbosité du langage n’est plus à démontrer et pour alléger la charge, des efforts sont faits, notamment avec de l’inférence de type avec le mot clé `var` au moment de déclarer une variable. Les records vont dans ce sens-là mais en plus permettent de transporter de la donnée de manière immutable. Voici un petit exemple de code : ```java public record Bouchot(String nom){} ``` C’est tout, une seule ligne pour créer un record ! Derrière, le langage se débrouille pour générer les méthodes `equals()`, `toString()` et `hashCode()`. Pour accéder au nom d’un bouchot (que l’on aura déclaré comme ceci : `var monBouchot = new Bouchot("DLFP")`, il suffit de faire `monBouchot.nom()`. Un record reste une classe Java, avec quelques limitations toutefois, il est donc possible de rajouter des contraintes sur le constructeur, par exemple refuser les `null` ou écrire votre propre méthode `toString()`, si la version générée ne vous convient pas. ```java public record Bouchot(String nom){ public Bouchot { if(null == nom){ throw new IllegalArgumentException("Crois-tu qu’une moule puisse s’accrocher à null ?"); } } } ``` Une chose intéressante à savoir est que les records ont été définis en prenant en compte le futur du langage aussi bien du côté développeur (types scellés, pattern matching et déconstruction) que de la machinerie interne (inline type). Pour aller plus loin, je vous laisse lire cet [article](https://www.infoq.com/articles/java-14-feature-spotlight/). ## Pattern Matching instanceof ## Java est le dernier des principaux langages fonctionnant sur la JVM à ne pas implémenter le pattern matching. Pour cette première, il commence modestement en apportant un peu plus de légèreté syntaxique. ```java // Avant avec le cast if (obj instanceof Bouchot) { Bouchot b = (Bouchot) obj; var nom = b.nom(); } //Après if (obj instanceof Bouchot b) { var nom = b.nom(); } ``` L’étape suivante est de combiner le _pattern matching_ avec les _switch expressions_ et de pouvoir déconstruire les records. Tout le programme est détaillé dans cette [JEP](https://openjdk.java.net/jeps/375) et en voici un avant-goût : ```java record Point(int x, int y) {} if (obj instanceof Point(var a, var b)) { System.out.println(a+b); } ``` ## Outils de packaging ## L’idéal de Java qui était, si je ne me trompe pas, compile ton application une seule fois et elle fonctionnera partout, semble ne plus trop être à l’ordre du jour. La démarche est plus, livre ton application avec les modules Java strictement nécessaires et, optionnellement, le JRE qui va avec. Le but étant la diminution de la taille des applications mais aussi l’amélioration de la réactivité et de la sécurité, car moins de code sera chargé et donc exposé. Comme d’habitude, les détails sont à lire dans la [JEP 343](https://openjdk.java.net/jeps/343). ## Les blocs de texte, seconde itération ## Après une première implémentation dans la version précédente de Java, en voici une nouvelle ([JEP 368](https://openjdk.java.net/jeps/368)). Le principe est d’avoir une solution élégante pour se débarrasser des concaténations sur plusieurs lignes, de se battre avec les échappements divers et variés et surtout améliorer la lisibilité. ```java String query = """ SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` WHERE `CITY` = 'INDIANAPOLIS' ORDER BY `EMP_ID`, `LAST_NAME`; """; ``` C’est aussi utilisable en combinaison avec d’autres bibliothèques, voici un exemple d’utilisation avec [JOOQ](https://blog.jooq.org/2020/03/05/using-java-13-text-blocks-for-plain-sql-with-jooq). Avec cette seconde version, viennent des caractères d’échappement des lignes pour jongler avec les lignes trop longues ou gérer plus finement les retours à la ligne. Dans l’exemple suivant toutes les lignes feront 6 caractères : ```java String colors = """ red \s green\s blue \s """; ``` # Les mises au placard et les départs # Ironie de l’histoire, l’architecture créée par la même société que le langage est déprécié et ne sera rapidement plus supportée. Pour les rares qui ont encore du Solaris et du SPARC, c’est bientôt la fin pour faire tourner des JVM modernes. Les détails sont présents dans la [JEP 362](https://openjdk.java.net/jeps/362). Même process pour le ramasse-miettes Concurrent Mark Sweep dans le but de laisser plus de temps au développement des autres ramasse-miettes, les détails dans la [JEP 291](https://openjdk.java.net/jeps/291). Suppression des outils Pack200, suite à leur placardisation dans Java 11, ils sont remplacés par les outils `jlink`, plus d’informations dans la [JEP 367](https://openjdk.java.net/jeps/367). # Dans les prochaines versions # La seconde version du pattern matching de instanceof, les types scellés, la version définitive des blocs de texte. Je vous laisse regarder [cette diapo](https://github.com/forax/do-synthetic-methods-dream-of-electric-switch-expressions/blob/master/slideshow/chapter09-future.ipynb) de Rémi Forax (un contributeur OpenJDK). Pour une vision plus « sous le capot » Brian Goetz (un architecte du langage) a rédigé [deux articles](http://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-background.html) sur les évolutions de Java.