IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

JDK 23 : attendue pour septembre, Java 23 va s'accompagner d'une seconde preview de l'API de fichier de classe, du collecteur de flux ainsi que d'autres fonctionnalités

Le , par Stéphane le calme

1PARTAGES

14  0 
Le kit de développement Java (JDK) 23, la prochaine version prévue de Java standard, devrait être publiée en disponibilité générale le 19 septembre. Au programme, quatre fonctionnalités sont proposées pour JDK 23 : une API Vector (qui sera incubée pour la huitième fois); une deuxième preview de Stream Gatherers (collecteurs de flux) dont la première preview a été livrée avec JDK 22; une deuxième preview d'une API de fichier de classe et une preview des types primitifs dans les patterns, instanceof et switch.

L'API vectorielle

L'API vectorielle a été incubée dans les versions précédentes de Java depuis la sortie du kit de développement Java 16 jusqu'à la dernière version 22. Cette nouvelle version introduira une API pour aider à exprimer les calculs vectoriels qui se compilent au moment de l'exécution. L'objectif est de disposer d'instructions vectorielles optimales sur les différentes architectures de CPU prises en charge.

Parmi les objectifs de cette proposition figurent :
  • la fourniture d'une API claire et concise : l'API doit être capable d'exprimer de manière claire et concise une large gamme de calculs vectoriels consistant en des séquences d'opérations vectorielles composées de boucles et éventuellement d'un flux de contrôle. Il doit être possible d'exprimer un calcul générique en ce qui concerne la taille du vecteur ou le nombre de voies par vecteur, ce qui permet à ces calculs d'être portables sur du matériel prenant en charge différentes tailles de vecteurs.
  • la fourniture d'une plateforme agnostique : l'API doit être agnostique vis-à-vis de l'architecture du processeur, permettant des implémentations sur de multiples architectures supportant les instructions vectorielles. Comme c'est habituellement le cas pour les API Java, lorsque l'optimisation de la plate-forme et la portabilité sont en conflit, la portabilité de l'API est privilégiée, même si cela se traduit par des idiomes spécifiques à la plate-forme qui ne peuvent pas être exprimés dans un code portable.
  • une compilation et des performances fiables du runtime sur les architectures x64 et AArch64 : sur les architectures x64 capables, le runtime Java, en particulier le compilateur HotSpot C2, devrait compiler les opérations vectorielles avec les instructions vectorielles efficaces et performantes correspondantes, telles que celles prises en charge par les extensions SIMD en continu (SSE) et les extensions vectorielles avancées (AVX). Les développeurs doivent avoir la certitude que les opérations vectorielles qu'ils expriment seront étroitement liées aux instructions vectorielles correspondantes. Sur les architectures ARM AArch64, C2 compilera de la même manière les opérations vectorielles avec les instructions vectorielles supportées par NEON et [REV].
  • dégradation progressive : Il arrive qu'un calcul vectoriel ne puisse pas être entièrement exprimé au moment de l'exécution sous la forme d'une séquence d'instructions vectorielles, peut-être parce que l'architecture ne prend pas en charge certaines des instructions requises. Dans ce cas, l'implémentation de l'API vectorielle doit se dégrader de manière gracieuse et continuer à fonctionner. Cela peut impliquer l'émission d'avertissements si un calcul vectoriel ne peut pas être compilé efficacement en instructions vectorielles. Sur les plates-formes dépourvues de vecteurs, la dégradation gracieuse produira un code compétitif avec des boucles déroulées manuellement, où le facteur de déroulement est le nombre de voies dans le vecteur sélectionné.
  • Alignement sur le projet Valhalla ; L'objectif à long terme de l'API vectorielle est de tirer parti des améliorations apportées par le projet Valhalla au modèle d'objet Java


Exemple d'illustration

Voici un calcul scalaire simple sur des éléments de tableaux (il est supposé que les arguments du tableau sont de même longueur) :

Code Java : Sélectionner tout
1
2
3
4
5
void scalarComputation(float[] a, float[] b, float[] c) {
   for (int i = 0; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
   }
}

Voici un calcul vectoriel équivalent, utilisant l'API Vector :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
 
void vectorComputation(float[] a, float[] b, float[] c) {
    int i = 0;
    int upperBound = SPECIES.loopBound(a.length);
    for (; i < upperBound; i += SPECIES.length()) {
        // FloatVector va, vb, vc;
        var va = FloatVector.fromArray(SPECIES, a, i);
        var vb = FloatVector.fromArray(SPECIES, b, i);
        var vc = va.mul(va)
                   .add(vb.mul(vb))
                   .neg();
        vc.intoArray(c, i);
    }
    for (; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
    }
}

Collecteurs de flux et API de flux

Si vous vous souvenez, Stream Gatherers a été présenté en preview dans la version 22 du Java Development Kit. Il va maintenant être entièrement ajouté au JDK 23. Les Stream Gatherers amélioreront l'API Stream pour prendre en charge les opérations personnalisées. Ils permettront également aux pipelines de flux de transformer les données d'une manière qui est plus difficile à réaliser avec les opérations intégrées actuelles. L'objectif est de rendre les pipelines de flux beaucoup plus flexibles et expressifs, ce qui permettra également aux opérations personnalisées de manipuler des flux de taille infinie.

Types primitifs dans les modèles

JDK 23 s'accompagne d'une autre fonctionnalité en preview qui vaut la peine d'être notée pour les développeurs Java. Il s'agit des types primitifs dans les patterns, instanceof et switch.

En fait, de nombreuses restrictions relatives aux types primitifs imposent des frictions lors de l'utilisation de la recherche de patterns, de instanceof et du switch. L'élimination de ces restrictions rendrait le langage Java plus uniforme et plus expressif.

La première restriction est que la recherche de patterns pour switch ne prend pas en charge les patterns de type primitif, c'est-à-dire les patterns de type qui spécifient un type primitif. Seuls les patterns de type qui spécifient un type de référence sont pris en charge, tels que case Integer i ou case String s (depuis Java 21, les patterns d'enregistrement sont également pris en charge pour switch).

Grâce à la prise en charge des patterns de types primitifs dans switch, nous pouvons améliorer l'expression des switch
Code Java : Sélectionner tout
1
2
3
4
5
6
switch (x.getStatus()) {
    case 0 -> "okay";
    case 1 -> "warning";
    case 2 -> "error";
    default -> "unknown status: " + x.getStatus();
}

en transformant la clause default en une clause case avec un modèle de type primitif qui expose la valeur correspondante :

Code Java : Sélectionner tout
1
2
3
4
5
6
switch (x.getStatus()) {
    case 0 -> "okay";
    case 1 -> "warning";
    case 2 -> "error";
    case int i -> "unknown status: " + i;
}

Prendre en charge des patterns de type primitif permettrait également aux gardes d'inspecter la valeur correspondante :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
switch (x.getYearlyFlights()) {
    case 0 -> ...;
    case 1 -> ...;
    case 2 -> issueDiscount();
    case int i when i >= 100 -> issueGoldCard();
    case int i -> ... appropriate action when i > 2 && i < 100 ...
}

En somme, cette fonctionnalité améliorera considérablement la recherche de patterns en autorisant les patterns de type primitif dans les contextes de patterns. Elle va également fournir des constructions faciles à utiliser qui éliminent le risque de perdre des informations en raison de casts non sûrs. Suite aux améliorations apportées à switch dans Java 5 ( switch enum) et Java 7 ( switch string), elle va permettre à switch de traiter des valeurs de n'importe quel type primitif.


Class-File API

L'utilisation de l'API class-file vise à fournir une API pour le traitement des fichiers de classe qui suit le format de fichier de classe défini par la spécification de la machine virtuelle Java. Cela signifie également que cela permettrait aux composants du JDK de migrer vers l'API standard et de supprimer la copie de la bibliothèque ASM du kit de développement Java. L'API de fichier de classe ajoute quelques ajustements, notamment la rationalisation de la classe CodeBuilder, qui contient par défaut des méthodes d'usine pour les instructions de bytecode, y compris des usines de bas niveau, des usines de niveau moyen et des constructeurs de haut niveau pour les blocs de base. Vous trouverez ci-dessous un exemple de code d'un CodeBuilder ASM et d'un CodeBuilder Java.

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

Code Java : Sélectionner tout
1
2
3
4
5
6
void fooBar(boolean z, int x) {
    if (z)
        foo(x);
    else
        bar(x);
}

Avec ASM, nous pourrions générer la méthode comme suit :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ClassWriter classWriter = ...;
MethodVisitor mv = classWriter.visitMethod(0, "fooBar", "(ZI)V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 1);
Label label1 = new Label();
mv.visitJumpInsn(IFEQ, label1);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "Foo", "foo", "(I)V", false);
Label label2 = new Label();
mv.visitJumpInsn(GOTO, label2);
mv.visitLabel(label1);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "Foo", "bar", "(I)V", false);
mv.visitLabel(label2);
mv.visitInsn(RETURN);
mv.visitEnd();

Dans ASM, le MethodVisitor sert à la fois de visiteur et de constructeur. Les clients peuvent créer un ClassWriter directement, puis lui demander un MethodVisitor. L'API Class-File inverse ce principe : Au lieu de créer un constructeur avec un constructeur ou une fabrique, le client fournit une lambda qui accepte un constructeur :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ClassBuilder classBuilder = ...;
classBuilder.withMethod("fooBar", MethodTypeDesc.of(CD_void, CD_boolean, CD_int), flags,
                        methodBuilder -> methodBuilder.withCode(codeBuilder -> {
    Label label1 = codeBuilder.newLabel();
    Label label2 = codeBuilder.newLabel();
    codeBuilder.iload(1)
        .ifeq(label1)
        .aload(0)
        .iload(2)
        .invokevirtual(ClassDesc.of("Foo"), "foo", MethodTypeDesc.of(CD_void, CD_int))
        .goto_(label2)
        .labelBinding(label1)
        .aload(0)
        .iload(2)
        .invokevirtual(ClassDesc.of("Foo"), "bar", MethodTypeDesc.of(CD_void, CD_int))
        .labelBinding(label2);
        .return_();
});

Dans le Java Development Kit 23, le Java CodeBuilder supprime les méthodes de niveau intermédiaire qui font double emploi avec les méthodes de bas niveau ou qui, dans le pire des cas, ne sont pas souvent utilisées. Vous renommez les méthodes de niveau intermédiaire restantes afin d'en améliorer la convivialité. L'équipe a également affiné le modèle de classe ClassSignature. Cela signifie qu'il a été amélioré pour modéliser plus précisément les signatures génériques des superclasses et des superinterfaces. Selon la proposition OpenJDK à l'origine de cette fonctionnalité, la plateforme Java devrait définir et mettre en œuvre une API de fichier de classe standard qui évolue en même temps que le format de fichier de classe, qui peut facilement changer ou évoluer tous les six mois environ.

Un certain nombre d'autres fonctionnalités présentées en preview dans le JDK 22 pourraient être intégrées dans le JDK 23

Il s'agit notamment des déclarations avant super(...), qui donneraient aux développeurs une plus grande liberté dans l'expression du comportement des constructeurs ; des modèles de chaînes, qui faciliteraient l'expression de chaînes comprenant des valeurs calculées au moment de l'exécution ; des valeurs scopées, qui permettraient le partage de données immuables au sein et entre les threads ; et des classes et méthodes principales d'instance déclarées implicitement, qui permettraient aux développeurs débutants d'écrire plus facilement des programmes sans avoir besoin de comprendre les caractéristiques du langage conçues pour les programmes de grande taille.

Le responsable de Java, Oracle, a également dévoilé ses projets pour Java en 2024. Oracle a présenté des améliorations qui impliquent des projets OpenJDK allant d' Amber, pour le développement de petites fonctionnalités axées sur la productivité, à Babylon, pour l'extension de Java à des modèles de programmation étrangers tels que les GPU, en passant par Valhalla, pour l'augmentation du modèle d'objet Java avec des objets de valeur afin d'éliminer les goulets d'étranglement de longue date en matière de performances.

Sources : OpenJDK, projets (Amber, Babylon, Valhalla)

Et vous ?

Que pensez-vous des éléments proposés par le JDK 23 ? Lesquels vous intéressent le plus et pourquoi ?
Quel impact pensez-vous que les collecteurs de flux auront sur la manière dont vous traitez les données en Java ?
Comment l’API de fichier de classe pourrait-elle changer la façon dont vous interagissez avec le bytecode Java ?
Quelles sont les fonctionnalités que vous aimeriez voir dans les prochaines versions de Java ?

Une erreur dans cette actualité ? Signalez-nous-la !