Java 20 : quoi de neuf ?
Maintenant que Java 20 est features complete (Rampdown Phase One au jour d’écriture de l’article), c’est le moment de faire le tour des fonctionnalités qu’apporte cette nouvelle version, à nous, les développeurs.
Cet article fait partie d’une suite d’article sur les nouveautés des dernières versions de Java, pour ceux qui voudraient les lire en voici les liens : Java 19, Java 18, Java 17, Java 16, Java 15, Java 14, Java 13, Java 12, Java 11, Java 10, et Java 9.
Force est de constater qu’il n’y a pas grand-chose de nouveau dans cette release. Des 6 Java Enhancement Proposals (JEP) qui ont été délivrées, une seule porte sur une nouvelle API du langage : JEP 429 – Scoped Values, les autres sont des itérations de fonctionnalités existantes.
JEP 429 – Scoped Values
La JEP 429 introduit les Scoped Values qui permettent le partage de données immuables au sein et entre les threads.
Jusqu’ici, pour partager une donnée au sein d’un thread, on utilisait des Thread Locals. Ceux-ci sont mutables et nécessitent une structure de donnée complexe dont le coût n’est pas en accord avec les virtual threads qui sont légers et peu chère à construire. De plus, ils étaient parfois la cause de bug, car leur utilisation n’était pas forcément bien comprise.
Le support des thread locals avec Loom étant problématique, une nouvelle manière de partager de la donnée entre thread a été proposée qui est immuable et s’interface mieux avec l’API Structured Concurrency : les Scoped Values.
Voici un exemple très basique :
private static final ScopedValue<String> USERNAME = ScopedValue.newInstance(); ScopedValue.where(USERNAME, "duke") // bind a value to the scope // start a thread that could access this value .run(() -> System.out.println("User: " + USERNAME.get()));
Il est aussi possible d’appeler .call()
avec un Callable
pour retourner une valeur.
A la fin de l’appel de la méthode run()
, le scoped value n’aura plus de valeur pour le thread, ce qui évite tout risque de memory leak.
Plus d’information dans la JEP 429.
JEP-432 – Record Patterns (Second Preview)
La seconde preview des Record Patterns contient des changements importants, elle mérite donc un paragraphe dédié pour les présenter.
Si vous ne connaissez pas encore les Record Patterns, vous pouvez vous référer à mon article Java 19 : Quoi de neuf ?
Trois changements ont été faits :
- Support de l’inférence du type paramétré pour les patterns de record generic.
- Support des record patterns dans les boucles for (enhanced for statements).
- Suppression du support des patterns de record nommés.
Le support de l’inférence du type paramétré pour les patterns de record generic permet d’omettre le type paramétré dans un record pattern si celui-ci peut être inféré par le compilateur. Étonnamment, l’opérateur diamond (<>) n’a pas été utilisé, on peut simplement omettre le type paramétré.
record Box<T>(T t) {} static void test(Box<String> bo) { if (bo instanceof Box(var s)) { // Inferred to be Box<String>(var s) System.out.println("String " + s); } }
Le support des record patterns dans les boucles for (enhanced for statements) permet l’accès direct aux composants d’un record dans le corps d’une boucle for.
record Point(int x, int y) {} static void dump(Point[] pointArray) { for (Point(var x, var y) : pointArray) { // Record Pattern in header! System.out.println("(" + x + ", " + y + ")"); } }
Avant la JEP 432, il était possible de nommer un pattern de record pour, en quelque sorte, faire du pattern matching sur le record et ses composants en même temps.
record Point(int x, int y) {} static void noMorePossible(Point p) { if(p instanceof Point(int x, int y) p) { System.out.println("x=" + x + " y=" + y + "for the point " + p); } }
Cette possibilité a été supprimée, le code ci-dessus générera maintenant une erreur à la compilation. On ne sait pas encore si c’est définitif ou si elle pourrait revenir prochainement.
Plus d’information dans la JEP 432.
Les fonctionnalités qui restent en preview
Les fonctionnalités suivantes restent en preview (ou en incubator module).
- JEP-433 – Pattern Matching for switch (Fourth Preview) : changement de l’exception lancée par un switch d’une enum dans le cas où le switch est censé être exhaustif (un case pour chaque valeur de l’enum) mais ne l’est pas au runtime. L’exception était de type
IncompatibleClassChangeError
et sera maintenant de typeMatchException
. - JEP-434 – Foreign Function & Memory API (Second Preview) : évolution mineure de l’API pour en faciliter l’utilisation.
- JEP-436 – Virtual Threads (Second Preview) : pas de changement significatif, la fonctionnalité reste en preview pour avoir plus de retour sur son utilisation.
- JEP-437 – Structured Concurrency (Second Incubator) : ajout de l’héritage des scoped values (JEP 429)
- JEP 438 – Vector API (Fifth Incubator) : petits bugfixes et amélioration de performance.
Pour les détails sur celles-ci, vous pouvez vous référer à mes articles précédents.
Divers
Divers ajouts au JDK :
- Tous les constructeurs de la classe URL ont été dépréciés, il faut préférer la classe URI à la classe URL et, si nécessaire, utiliser
URI.toURL()
pour construire un objet de type URL depuis une URI. - Matcher.hasMatch(), MatchResult.end(String), MatchResult.group(String), MatchResult.start(String)
Float.float16ToFloat(short)
: Renvoie la valeur flottante la plus proche de la valeur numérique de l’argument, qui est une valeur binary16 à virgule flottante encodée dans un short.Float.floatToFloat16(float)
: Renvoie la valeur binary16 à virgule flottante, encodée dans le short le plus proche de l’argument.
La totalité des nouvelles API du JDK 20 peuvent être trouvées dans The Java Version Almanac – New APIs in Java 20.
Des changements internes, de la performance, et de la sécurité
Chaque nouvelle version du JDK apporte ses optimisations de performances (entre autres GC et méthodes intrisics), et de sécurité. Celle-ci ne fait pas défaut.
Côté performance, on peut noter plusieurs améliorations dans la gestion des I/O via les classes BufferedInputStream
, PushbackInputStream
, SequenceInputStream
et ZipInputStream
.
Thomas Schatzl a contribué une amélioration du garbage collector G1 qui n’utilise maintenant plus qu’un seul bitmap pour stocker les informations de liveness des objets de la heap. Ce changement fait baisser la taille de la mémoire native utilisée par G1 d’exactement 1.5% de la taille de la heap. Plus d’information dans cet article qui contient aussi un rappel assez complet sur les différentes phases de G1 : Concurrent Marking in G1. D’autres changements ont été fait côté Garbage Collector, vous pouvez les retrouver dans cet article de Thomas Schatzl : JDK 20 G1/Parallel/Serial GC changes.
Côté sécurité, le focus a été mis sur le renforcement de la sécurité de la JVM, les performance des algorithmes de cryptographie, ainsi que l’ajout d’évènements JFR pour le monitoring de la sécurité. Vous pouvez vous référer à l’article de Sean Mullan pour une liste exhaustive des changements de sécurité inclus cette version : JDK 20 Security Enhancements.
Conclusion
Cette nouvelle version apporte quelques changements mineurs au pattern matching, et quelques améliorations dans la programmation concurrente via la nouvelle API Scoped Values. Donc très peu de choses par rapport aux précédentes versions. La prochaine version sera aussi la prochaine LTS, on espère que pour celle-ci, une partie des JEP qui sont en preview depuis littéralement des années seront promues stable.