Une tasse de café blanche, simple vue de face dans une soucoupe avec de la fumée qui s'échappe

Java 22 : quelles sont les nouveautés majeures à connaître ?

Développeurs Java, réjouissez-vous : Java 22 est arrivé ! Il s’agit de la version majeure après la LTS Java 21, et elle promet de révolutionner votre façon de coder.
Découvrez en détail les nouvelles fonctionnalités, l'évolution des fonctionnalités en preview et les fonctionnalités dépréciées.

Nouvelles fonctionnalités de Java 22

JEP 456 : Unnamed Variables & Patterns

Pendant nos développements quotidiens, nous déclarons parfois des variables que nous n’avons pas l’intention d’utiliser, que ce soit pour des raisons de style de code ou parce que le langage nécessite des déclarations de variables dans certains contextes. L’intention de non-utilisation est connue au moment où le code est écrit, mais si docker n’est pas capturée explicitement, les responsables ultérieurs pourraient accidentellement utiliser la variable, violant ainsi l’intention. Si nous pouvions rendre impossible l’utilisation accidentelle de telles variables, le code serait plus informatif, plus lisible et moins sujet aux erreurs.

Les variables et modèles sans nom ont été publiés dans Java 21 en tant que preview sous le nom "pattern et variables sans nom" et seront finalisés dans Java 22 par la proposition d’amélioration 456 du JDK sans aucune modification.

Variables inutilisées

Par exemple, la plupart des développeurs ont écrit des blocs catch de cette forme, où le paramètre d’exception ex n’est pas utilisé :

JEP 456 : Unnamed Variables & Patterns -Variables inutilisées


Avec Unnamed var :

JEP 456 : Unnamed Variables & Patterns - Variables inutilisées avec Unnamed var


Les variables sans nom peuvent être utilisées dans plusieurs catch blocs :

JEP 456 : Unnamed Variables & Patterns - utilisées dans plusieurs catch blocs


Dans try-avec-ressources :

Java 22 - Variables inutilisées - Try-avec-ressources


Une lambda dont le paramètre n’est pas pertinent :

Java 22 - Une lambda dont le paramètre n’est pas pertinent


Variables Pattern inutilisées

Les variables locales peuvent être déclarées aussi par des type patterns - comme les variables locales connues comme des variables locales pattern- les type patterns aussi peuvent déclarer des variables inutilisées. Considérons le code suivant, qui utilise type patterns dans le case label dans une switch :

Java 22 - Variables Pattern inutilisées


Avec Unnamed var :

Java 22 - Avec Unnamed var


JEP 458 : Launch Multi-File Source-Code Programs

Depuis Java 11, il est possible de lancer un programme depuis un fichier source .java sans étape de compilation préalable. Le launcher Java va alors compiler le programme en mémoire automatiquement avant son exécution.
Avec la JEP 458, il est maintenant possible de lancer un programme depuis un fichier source qui utilise une classe définie dans un autre fichier source. Ce second fichier sera aussi compilé automatiquement en mémoire. Les fichiers sources sont recherchés dans la hiérarchie de répertoire habituelle en Java qui reflète la structure des packages.
Seuls les fichiers sources utilisés par le programme principal seront compilés en mémoire.

EP 454 : Foreign Function & Memory API

Néanmoins, un changement important a été fait dans l’API Foreign Function & Memory : l’introduction de la notion de méthode limitée (restricted). Certaines méthodes de cette nouvelle API sont marquées comme limitées : pour les utiliser, il faudra utiliser l’option ligne de commande –enable-native-access=module-name. Pour l’instant, l’accès à des méthodes limitées génère un warning, mais il se pourrait que leur accès soit interdit dans une future version de la JVM. Les méthodes limitées sont utilisées pour binder une fonction native et/ou une donnée native, ce qui est par nature unsafe. C’est pour cela que son accès doit être donné spécifiquement via une option ligne de commande.

Le code suivant appelle la strlen()fonction de la bibliothèque standard C pour déterminer la longueur de la chaîne « Happy Coding ! » :

Java 22 - EP 454 : Foreign Function & Memory AP


Java 22 : Features en preview ou incubation

Ces fonctionnalités sont encore en développement. Par défaut, elles ne sont pas accessibles et doivent être activées manuellement.
Les fonctionnalités peuvent, dans une future release, être supprimées ou évoluer.

JEP 447 - Instructions avant super (…) (Preview)

Avant Java 22, quand une classe étend une autre classe et veut appeler le constructeur de la classe parente dans son propre constructeur, la JVM oblige l’appel du constructeur parent à être la première instruction du constructeur de la classe parente. Ceci permet de s’assurer que tous les champs de la classe parente sont initialisés avant la construction de la classe enfant.

Il s’agit d’une fonctionnalité en preview qui autorise des instructions avant l’appel du constructeur parent tant que ceux-ci n’accèdent pas à l’instance en cours de création.

Validation des paramètres, pré-calculs d’arguments

Java 22 -JEP 447 - Instructions avant super (…) (Preview)


Avec la JEP 447 :

Java 22 - Avec la JEP 447


Le code est plus lisible et évite potentiellement l’exécution inutile du construction parent en cas de non-validité de l’argument.

Préparation des arguments du constructeur de superclasse

Parfois, nous devons faire des traitements non triviaux avant l’appel du constructeur de la super classe. Nous pouvons le faire en se référant à des méthodes auxiliaires.
Dans l’exemple qui suit, le constructeur de la superclasse prend en paramètre un tableau byte[] et le constructeur de la subclasse prend en paramètre une chaîne String.

Java 22 - Préparation des arguments du constructeur de superclasse


Ce code serait plus lisible si nous pouvions intégrer le code de préparation des arguments directement dans le constructeur de la classe fille.

Java 22 - code de préparation des arguments directement dans le constructeur de la classe fille


JEP 461 : Stream Gatherers (Preview)

Voici une amélioration de l’API Stream en introduisant le support d’opérations intermédiaires personnalisées qui est actuellement en cours de développement. Cette fonctionnalité est actuellement offerte en version préliminaire.
L’API Stream existante propose un ensemble défini d’opérations intermédiaires et terminales. Elle permet d’étendre les opérations terminales via la méthode Stream : collect(Collector), mais ne permet pas d’étendre les opérations intermédiaires. Certaines fonctionnalités nécessitent la combinaison de plusieurs opérateurs intermédiaires pour être réalisées et parfois, nous ne parvenons pas à un produire un flux répondant exactement au besoin.
Avec la JEP 461, il est désormais possible de définir ses propres opérations intermédiaires via Stream : :gather(Gatherer).

Rappel sur la composition du stream

  1. Stream source : la source qui génère le stream. (exp via IntStream.of(…) ou Collection.stream()).
  2. Intermediate operations : les opérateurs intermédiaires qui transforment le Stream. (ex map(…), filter(…), et limit(…)).
  3. Terminal operations : opérations qui résume le stream soit par l’intermédiaire des Collectors ou bien par des d’autres opérateurs comme la réduction (reduce()) , compteur (count()) etc…

Interface Gatherer <T,A,R>

  • T : le type d’éléments d’entrée dans l’opération de collecte
  • A : le type d’état potentiellement mutable de l’opération de collecte (souvent caché en tant que détail d’implémentation)
  • R : le type d’éléments de sortie de l’opération de collecte L’interface java.util.stream.Gatherer définit les méthodes suivantes :
    - initializer() : optionnel, permet de maintenir un état lors du traitement des éléments.
    integrator() : intègre un nouvel élément depuis la stream entrante, et émet si nécessaire un élément dans la stream de sortie.
    combiner() : optionnel, peut être utilisé pour évaluer le gatherer en parallèle pour les stream parallèle.
    finisher() : optionnel, appelé quand la stream n’a plus d’élément en entrée.

Gatherer prédéfinis :

JAVA 22 introduit les rassembleurs intégrés suivants dans la classe java.util.stream.Gatherers :

fold : opération qui effectue une transformation ordonnée, semblable à une réduction, pour des scénarios dans lesquels aucune fonction de combinaison ne peut être implémentée, ou pour des réductions qui dépendent intrinsèquement de l’ordre.

Java 22 - Gatherer prédéfinis


mapConcurrent : une opération qui exécute des opérations simultanément avec une fenêtre fixe de concurrence maximale, à l’aide de VirtualThreads. Cette opération préserve l’ordre du flux !

Java 22 - mapConcurrent


windowFixed : stateful many-to-many gatherer qui regroupe les éléments d’entrée dans des listes d’une taille fixe , émettant les fenêtres en sortie lorsqu’elles sont pleines.

Java 22 - windowFixed


windowSliding : stateful many-to-many gatherer qui regroupe les éléments d’entrée dans des listes d’une taille fournie. Après la première fenêtre, chaque fenêtre suivante est créée à partir d’une copie de son prédécesseur en supprimant le premier élément et en ajoutant l’élément suivant de la stream d’entrée.

Java 22 - windowSliding


scan : effectue une analyse de préfixe - une accumulation incrémentielle, à l’aide des fonctions fournies.

Java 22 - scan


On peut combiner autant de gatherers entre eux jusqu’à arriver à l’opérateur terminal : stream.gather(…).gather(…).collect(…);
Les Gatherer sont utilisés pour transformer et manipuler des éléments de flux dans les opérations intermédiaires, tandis que les Collecteurs sont utilisés pour finaliser la pipeline de flux avec les opérations de terminales.

JEP 457 : Class-File API (Preview)

Java 22 fournit une API standard pour analyser, générer et transformer les fichiers de classe Java. Cela servira initialement à remplacer ASM, le framework de manipulation et d’analyse du bytecode Java, caractérisé par son ancien/legacy codebase.
L’API Class-File, présente sous le package java.lang.classfile et ses sous-packages, intègre trois abstractions principales : éléments, builders, et transformations : 

  • Un élément est une description immuable d’une partie d’un fichier de classe ; il peut s’agir d’une instruction, d’un attribut, d’un champ, d’une méthode ou d’un fichier de classe entier. Certains éléments, comme les méthodes, sont composés ; en plus d’être des éléments, ils contiennent également des éléments qui leur sont propres et peuvent être traités dans leur ensemble ou bien décomposés.
  • Chaque type d’élément composé a un builder correspondant qui possède des méthodes de construction spécifiques (par exemple, ClassBuilder : :withMethod) facilitant la construction du fichier class.
  • Enfin, une transformation représente une fonction qui prend un élément et un builder et détermine comment cet élément est transformé en d’autres éléments.

Generating class files with builders

Supposons que nous souhaitions générer la méthode suivante dans un fichier de classe :

Java 22 -JEP 457 : Class-File API (Preview) - Generating class files with builders


Le bout de code suivant montre comment une méthode peut être générée à l’aide de Builder, démontrant l’approche spécifique et transparente de l’API en matière de génération de code :

Java 22 - JEP 457 : Class-File API (Preview) - Builder


Pour plus d’exemples et détails sur les fonctionnalités voir cette documentation

JEP 459 : String Templates (2nd Preview)

Il s’agit d’une fonctionnalité qui reste en preview et qui a pour but de gagner plus de feedback par rapport à Java 21.

En tant que développeurs, nous composons régulièrement des chaînes à partir d’une combinaison de texte littéral et d’expressions. Le langage Java et les API fournissent plusieurs mécanismes de composition de chaînes, même si, malheureusement, tous présentent des inconvénients (composition par opérateur + , StringBuilder etc.).

Beaucoup de langages supportent l’interpolation de chaînes de caractères. Le problème de l’interpolation est qu’elle est dangereuse en tant que fonctionnalité globale, car elle ne permet pas de validation (sanitization) lors de la construction de la chaîne de caractère finale. Elle s’expose donc, par exemple, aux injections SQL ou JavaScript.

Exemple d’interpolation en Java :

Java 22 - JEP 459 : String Templates (2nd Preview)


L’expression String s = "{firstName} {lastName}" ne compile pas pour des raisons de sécurité, du fait qu’il existe des librairies qui utilisent {} comme délimiteur (d’où l’utilisation de STR).

Les processeurs standards

  • RAW : processeur qui n’interpole pas les chaînes de caractère et qui permet des manipulations bas niveau.
  • STR : processeur qui interpole une chaîne de caractère vers une autre chaîne de caractère via concaténation simple.
  • FMT : processeur qui interpole une chaîne de caractère vers une autre chaîne de caractère en formatant les expressions via un Formatter, par exemple FMT."%05d{x} + %05d{y} = %05d\{x + y}";

JEP 462 : concurrence structurée (2nd preview)

La concurrence structurée a été proposée par JEP 428 et livrée dans le JDK 19 en tant qu’API d’incubation. Il a été réincubé par JEP 437 dans JDK 20 avec une mise à jour mineure pour hériter des valeurs étendues (JEP 429). Il a été présenté pour la première fois dans le JDK 21 via JEP 453. On propose ici de re-prévisualiser l’API dans le JDK 22, sans changement, afin d’obtenir plus de retours.

JEP 463 : Classes implicitement déclarées et méthodes main d’instance (2nd preview)

Le but de cette fonctionnalité est de fair évoluer le langage de programmation Java afin que les débutants en Java aient la possibilié d’écrire leurs premiers programmes sans avoir besoin de comprendre les fonctionnalités du langage conçues pour les grands programmes (elle est apparue en preview dans Java 21).

Tout d’abord, on propose une amélioration du protocole par lequel les programmes Java sont lancés pour autoriser les méthodes principales d’instance. De telles méthodes ne sont pas static, ne doivent pas nécessairement être public, et n’ont pas besoin d’avoir de paramètre.

Java 22 - JEP 463 : Classes implicitement déclarées et méthodes main d’instance (2nd preview)


Deuxièmement, on permet à un fichier source, de déclarer implicitement une classe (fichier HelloWorld.java) :

Java 22 - JEP 463 : Classes implicitement déclarées et méthodes main d’instance (2nd preview) -HelloWorld.java


Classes implicitement déclarées

Les spécificités suivantes s’appliquent aux classes implicites :

  • Une classe implicite se trouve toujours dans le package sans nom (tout comme une classe normale sans packagedéclaration).
  • Une classe implicite est toujours final.
  • Une classe implicite ne peut pas implémenter d’interfaces ni hériter d’autres classes. De même, aucune classe ne peut hériter d’une classe implicite.
  • Une classe implicite n’est pas accessible via le nom donné par le compilateur, c’est- à-dire que les autres classes ne peuvent pas instancier une classe implicite et ne peuvent pas y appeler de méthodes, pas même statiques.

Cependant, une classe implicite peut appeler des méthodes sur elle-même, comme dans l’exemple suivant :

Java 22 - JEP 463 : Classes implicitement déclarées et méthodes main d’instance (2nd preview) -HelloWorld.java


Puisqu’une classe implicite n’est pas accessible de l’extérieur, elle doit contenir une méthode main().

Méthodes Main d’instance

Les méthodes main d’instance sont des méthodes main non statiques. Les méthodes suivantes seront autorisées à l’avenir :

  • Méthodes d’instance non statiques,
  • méthodes avec le niveau de visibilité public, protected ou package-private (sans modificateur),
  • méthodes avec ou sans paramètres.

(String[]). Voici quelques exemples :

  • void main()
  • void main(String[] args)
  • public void main()
  • protected static void main(String[] args)

Les méthodes statiques et non statiques comprenant la même signature et les méthodes contenant différents modificateurs de visibilité avec la même signature s’excluent mutuellement et conduisent à une erreur de compilation « la méthode est déjà définie ».

Cependant, il peut y avoir une méthode principale avec un String[]paramètre et une méthode principale sans paramètres :

Java 22 - JEP 463 : Classes implicitement déclarées et méthodes main d’instance (2nd preview) - String[]paramètre et une méthode principale sans paramètres


Dans un tel cas, la méthode avec le paramètre (String[]) est démarrée.

JEP 464 : Scoped Values (2nd Preview)

Les Scopped values entrent également dans un deuxième cycle de preview dans Java 22 sans aucune modification par rapport à Java 21.

Les scopped values permettent de transmettre une ou plusieurs valeurs à une ou plusieurs méthodes sans les définir comme paramètres explicites et sans les transmettre d’une méthode à la suivante.

L’exemple suivant montre comment une API définit l’utilisateur connecté en tant que scopped value :

Java 22 - JEP 464 : Scoped Values (2nd Preview)


Supposons que le apiWebService appelé par le serveur appelle un service et que ce service, à son tour, appelle un Repository. Dans ce Repository, nous pourrions alors accéder à l’utilisateur connecté comme suit, par exemple :

Java 22 - JEP 464 : Scoped Values (2nd Preview)

 

JEP 460 : Vector API (7th Incubator)

La vector Vector API entre dans sa septième incubation L’API permettant d’exprimer des calculs vectoriels qui se compilent au moment de l’exécution en instructions vectorielles pour les architectures CPU prises en charge. Cette nouvelle version inclut des bugfixes et des améliorations de performance.

Java 22 : Dépreciation / Suppression

  • Thread.countStackFrames a été supprimé : La méthode Thread.countStackFrames() était déjà marquée comme « obsolète » dans Java 1.2 en 1998. Dans Java 9, elle était marquée comme « obsolète pour suppression » et depuis Java 14, elle génère un fichier UnsupportedOperationException. La méthode a été supprimée dans Java 22.
  • L’ancienne implémentation de Core Reflection a été supprimée : Dans Java 18, le mécanisme de réflexion principal a été réimplémenté sur la base des handles de méthode. Cependant, l’ancienne fonctionnalité était toujours disponible et pouvait être activée via l’option VM -Djdk.reflect.useDirectMethodHandle=false. Dans Java 22, l’ancienne fonctionnalité est complètement supprimée et l’option VM mentionnée ci-dessus est ignorée.
  • Dépréciations et suppressions dans sun.misc.Unsafe

Il est à noter qu’aucun JEP n’existe pour ces changements, on peut les retrouver dans le bug tracker.

Java 22 n’est pas seulement une version de stabilisation après la version 21 qui est LTS mais aussi une version qui introduit des ajouts majeurs tels que les Stream Gatherer, quelques JEPs et la sortie de preview de la Foreign Function & Memory API qui va permettre une utilisation simplifiée de fonctions natives en Java avec une API performante et plus facile d’utilisation que JNI. De plus, nous pouvons noter l’introduction de la feature de variable anonymes.

Vous souhaitez échanger avec un expert ?

Contactez-nous