SlideShare une entreprise Scribd logo
1
les nouveautés de
Java 22
@jmdoudoux
JMdoudouX
8 avril 2024
Sciam
Java SE en 2024
2
Java poursuit son évolution
En respectant le rythme de release tous les 6 mois
JDK 21 est la version LTS courante
Publiée le 19 septembre 2023
Depuis la réduction de 3 à 2 ans entre 2 LTS après Java 17
OpenJDK 21 est l’implémentation de référence
Des spécifications de la version 21 de Java SE
Des patchs seront disponibles
Au moins jusqu’à la prochaine version LTS
Le JDK 22 a été le 19 mars 2024
Sciam
Java 22
Les spécifications dans la JSR 397
3
12 JEPs
Les nouvelles fonctionnalités
En Preview
JEP 457 : Class-File API (Preview)
JEP 461 : Stream Gatherers (Preview)
JEP 447 : Statements before super(...) (Preview)
Standard
JEP 458 : Launch Multi-File Source-Code Programs
JEP 423 : Region Pinning for G1
JEP 454 : Foreign Function & Memory API
Les fonctionnalités qui sortent de preview
JEP 456 : Unnamed Variables & Patterns
Les fonctionnalités qui restent en preview ou incubation
JEP 462 : Structured Concurrency (Second Preview)
JEP 464 : Scoped Values (Second Preview)
JEP 459 : String Templates (Second Preview)
JEP 460 : Vector API (Seventh Incubator)
JEP 463 : Implicitly Declared Classes and Instance Main Methods (Second Preview)
Sciam 4
Jean-Michel Doudoux
https://www.jmdoudoux.fr
@jmdoudoux
Co-fondateur du
Auteur de 2 didacticiels
Diffusés sous licence GNU FDL
• Développons en Java (4200 pages)
• Développons en Java avec Eclipse
Senior Tech Lead
chez
Sciam
Roadmap
Les fonctionnalités
du projet Amber
5
Les évolutions
du projet Loom
dans la JVM Hotspot
dans les API Java Core
du projet Panama
dans les outils du JDK
Sciam
Les fonctionnalités
du projet Amber
Unnamed Variables & Patterns
String Templates (Second Preview)
Implicitly Declared Classes and Instance Main Methods (Second Preview)
Statements before super(...) (Preview)
Sciam
Unnamed Variables & Patterns
En preview en Java 21 (JEP 443), en standard en Java 22
7
Unnamed pattern : un pattern inconditionnel qui ne correspond à rien
Utilisable dans un pattern imbriqué à la place d'un type ou record pattern
record Grade(String code, String designation) {}
record Employe(String nom, String prenom, Grade grade) {}
Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur"));
if (o instanceof Employe(var nom, var prenom, _)) {
System.out.println("Employe : " + nom + " " + prenom);
}
En utilisant le 51eme mot clé réservé de Java : _
But : Enrichir le langage d’une syntaxe
Pour les patterns inutilisés dans les records pattern imbriqués
Et les variables inutilisées qui doivent être déclarées
3 patterns, utilisables plusieurs fois dans la même portée
Sciam
Unnamed Variables & Patterns
Unnamed pattern variable :
Utilisable avec tous types de patterns
8
Unnamed variable : peut être initialisée mais non utilisée dans
Une variable locale dans un bloc
Une ressource dans un try-with-resources
L'en-tête d'une boucle for et for améliorée
Une exception d'un bloc catch
Un paramètre formel d'une expression Lambda
try (var _ = ScopedContext.acquire()) {
var _ = service.traiter((_, _) -> System.out.printn("traiter"));
} catch (Throwable _) { }
if (o instanceof Employe(var nom, var _, _)) {
System.out.println("Employe : " + nom);
}
Sciam
Unnamed Variables & Patterns
Utile dans des switchs avec des patterns sur des types scellés
9
void traiterFormeRonde(Forme forme) {
switch(forme) {
case Cercle c -> afficher(c);
case Carre c -> {}
case Rectangle r -> {}
}
}
switch(forme) {
case Cercle c -> afficher(c);
default -> {}
}
switch(forme) {
case Cercle c -> afficher(c);
case Carre _, Rectangle _ -> {}
}
Impossible d’avoir plusieurs patterns
nommés dans un case
Risque de bug en cas d’ajout d’un type :
sealed interface Forme permits Cercle, Carre, Rectangle {}
Utilisation possible de default
préférable d’utiliser des unnamed variables
Sciam
String Templates (Second Preview)
Courant de devoir créer des chaînes de caractères composées
À partir d'une combinaison de textes littéraux
Et de valeurs ou d'expressions
10
Historiquement plusieurs fonctionnalités, toutes avec inconvénients
String s = x + " + " + y + " = " + (x + y);
MessageFormat mf = new MessageFormat("{0} + {1} = {2}");
String s = mf.format(x, y, x + y);
String s = String.format("%2$d + %1$d = %3$d", x, y, x + y);
String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y);
String s = new StringBuilder()
.append(x)
.append(" + ")
.append(y)
.append(" = ")
.append(x + y)
.toString();
De nombreux langages proposent l'interpolation de chaînes
Comme alternative à la concaténation de chaînes
Première preview en Java 21 via la JEP 430
Sciam
String Templates (Second Preview)
La plupart des langages supportent l’interpolation de chaînes
Mais le résultat peut parfois engendrer des soucis indirects
Exemple : SQL ou JSON injection
11
En combinant :
- Un texte littéral avec des expressions intégrées
- Et un processeur de templates
Pour produire des chaînes de caractères construites dynamiquement
Avec la clarté de l’interpolation et un résultat plus sûr
Le but : enrichir le langage Java avec des string templates
Qui complètent les chaînes littérales et les blocs de texte
Possibilité de créer une instance de type quelconque
Avec un processeur de templates personnalisés
Sciam
String Templates (Second Preview)
Nouveau type d'expression dans le langage : les templates expressions
Pour effectuer une interpolation de chaîne pour créer une chaîne ou un objet
12
Syntaxiquement, ressemble à une chaîne littérale avec un préfixe :
String prenom = "Jean-Michel";
String message = STR."Bonjour {prenom}";
Une template expression est composée de trois éléments :
1) Un processeur de templates (STR)
2) Un caractère point (U+002E), celui utilisé dans les autres expressions
3) Un template ("Bonjour {prenom}")
qui contient une expression intégrée ({prenom})
Le template peut utiliser plusieurs lignes de code source
En utilisant une syntaxe similaire à celle des blocs de texte
Sciam
String Templates (Second Preview)
3 processeurs de templates dans le JDK
13
STR : effectue une interpolation pour créer une chaîne
int x = 10, y = 20;
String s = STR."{x} + {y} = {x + y}";
FMT : effectue une interpolation pour créer une chaîne
Il interprète les spécificateurs de format à gauche des expressions intégrées
Les spécificateurs de format sont ceux définis dans java.util.Formatter
RAW : produit un objet de type StringTemplate
String prenom = "Jean-Michel";
StringTemplate st = RAW."Bonjour {prenom}";
String message = STR.process(st);
Sciam
String Templates (Second Preview)
Possibilité de définir des processeurs de templates personnalisés
Pour générer des chaînes ou des objets qui peuvent être validés
14
Une instance de l'interface fonctionnelle StringTemplate.Processor
Implémenter l’unique méthode process()
Utilisation de la fabrique StringTemplate.Processor::of
Pour obtenir une instance
var JSON = StringTemplate.Processor.of((StringTemplate st) -> new JSONObject(st.interpolate()));
String nom = "Durant";
String prenom = "Pierre";
JSONObject doc = JSON."""
{
"nom": "{nom}",
"prenom": "{prenom}"
}""";
Sciam
String Templates (prochaine Preview)
Dans la JEP sur les fonctionnalités en preview
Il est prévu qu’elle puisse évoluer (voire même être supprimée)
En fonction des feed backs issus des previews
15
C’est le cas avec les String Templates
Une annonce est publiée sur la mailing list
Par Brian Goetz le 9 mars 2024
https://mail.openjdk.org/pipermail/amber-spec-experts/2024-March/004010.html
La fonctionnalité va profondément changer
Il va falloir attendre les détails
dans la prochaine JEP dans le JDK 23 24
Sciam
Implicitly Declared Classes and Instance Main
Methods (Second Preview)
16
Plutôt compliqué
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
Les buts :
Faire évoluer le langage pour simplifier les programmes simples
Et faciliter l’apprentissage des débutants avec le langage Java
Première preview dans le JDK 21 via la JEP 445
Sous le nom Unnamed Classes and Instance Main Methods (Preview)
Changement de nom de la JEP dans la seconde preview
Deux évolutions dans un fichier source
Sciam
Implicitly Declared Classes and Instance Main
Methods (Second Preview)
17
4 formes autorisées :
Statique ou d’instance
Avec ou sans paramètre
class HelloWorld {
void main() {
System.out.println("Hello world");
}
}
La méthode main()
Sélection de la méthode main() en 2 étapes :
Invocation d’une méthode candidate avec un paramètre String[], si existe
Sinon invocation d’une méthode candidate sans paramètres si existe sinon erreur
Pas d’ambiguïté car une méthode statique et d’instance ne peuvent pas avoir la même signature
Visibilité : public, protected, package-private (par défaut)
Sciam
Implicitly Declared Classes and Instance Main
Methods (Second Preview)
18
void main() {
System.out.println("Hello world");
}
Requière un constructeur par défaut
Créé à la compilation
Aucun autre constructeur possible
Déclaration implicite d’une classe dans le package par défaut
Dont il n’est pas possible de faire référence
Car son nom est déterminé à la compilation
Peut avoir des membres
Pas de référence de méthodes statiques
static String WORLD = "world";
void main() {
System.out.print("Hello");
Util.afficher(" "+WORLD);
}
class Util {
static void afficher(String message) {
System.out.println(message);
}
}
Le nom du fichier source est libre
Pas de Javadoc
Sciam
Statements before super(...) (Preview)
Historiquement la première instruction d’un constructeur
Est obligatoirement l’invocation d’un constructeur de la classe ou super-classe
Explicitement (this() ou super()) ou implicitement par le compilateur
19
Afin de garantir l’initialisation des champs
Requérant des solutions
Peu élégantes
public class MonEntierPositif extends MonEntier {
public MonEntierPositif(long valeur) {
super(valeur);
if (valeur < 0) throw new IllegalArgumentException("La valeur non positive");
}
}
public MonEntierPositif(long valeur) {
super(verifier(valeur));
}
private static long verifier(long valeur) {
if (valeur < 0) throw new IllegalArgumentException("La valeur non positive");
return valeur;
}
Parfois contraignant
Sciam
But : permettre d’avoir dans les constructeurs des traitements
Qui ne font pas référence à l'instance avant l'invocation explicite du constructeur
20
Dans ce prologue :
Impossible d’utiliser this.xxx ou super.xxx explicite ou implicite
Impossible de référencer un champ d’une classe englobante C.this.xxx
public class MonEntierPositif extends MonEntier {
public MonEntierPositif(long valeur) {
if (valeur < 0) throw new IllegalArgumentException("La valeur non positive");
super(valeur);
}
}
Statements before super(...) (Preview)
Sciam
Les fonctionnalités du
projet Loom
Structured Concurrency (Second Preview)
Scoped Values (Second Preview)
Sciam
Structured Concurrency (Second Preview)
Propose un nouveau modèle de programmation
Grâce au traitement de plusieurs tâches
Exécutées dans différents threads virtuels comme une seule unité de travail
22
Fork/Join et concurrence structurée sont complémentaires
Le but : simplifier la programmation multithread
En rationalisant la gestion des erreurs et l'annulation,
En améliorant la fiabilité et en renforçant l'observabilité
Fork/Join
Conçu pour traiter des tâches à forte intensité
de calcul sur une courte durée
Utilise des threads de l’OS
Complexe à mettre œuvre
Concurrence structurée
Conçue pour traiter des tâches à forte intensité
d'E/S
Utilise des threads virtuels
Facile à mettre en œuvre
Sciam
API en incubation en Java 20
Classe principale : java.util.concurrent.StructuredTaskScope
23
Le modèle permet une écriture du code dans un style synchrone
Avec une exécution en asynchrone
Le code est ainsi facile à écrire, à lire et à tester
API en preview en Java 21
Dans le package java.util.concurrent
Second preview en Java 22 sans modification
Structured Concurrency (Second Preview)
Sciam
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
return resultat;
}
 Créer une instance dans un try-with-resource
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
}
return resultat;
}
La mise en œuvre en plusieurs étapes
24
Structured Concurrency (Second Preview)
 Invoquer la méthode fork() pour chaque sous-tâche à exécuter
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
}
return resultat;
}
 Attendre la fin de l’exécution des sous-tâches
• Soit sans timeout en utilisant la méthode join()
• Soit avec timeout en utilisant la méthode joinUntil()
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15)); 
}
return resultat;
}
 Exploiter les résultats obtenus dans des instances de type Subtask
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15)); 
resultat = this.genererFacture(clientFuture.get(), commandeFuture.get()); 
}
return resultat;
}
Sciam
La classe StructuredTaskScope.ShutdownOnFailure
Propose un modèle invoke all
25
Facture getFacture(String codeClient, long idCommande)
throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask <Client> clientFuture = scope.fork(() -> this.getClient(codeClient));
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15));
scope.throwIfFailed();
resultat = this.genererFacture(clientFuture.get(), commandeFuture.get());
}
return resultat;
}
Qui exécute toutes les sous-tâches
Et termine toutes les sous-tâches en cours si une sous-tâche lève une exception
Structured Concurrency (Second Preview)
Sciam
La classe StructuredTaskScope.ShutdownOnSuccess
Propose un modèle invoke any
26
Temperature getTemperature(String ville) throws InterruptedException, ExecutionException {
Temperature resultat = null;
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Temperature>()) {
serviceMeteos.forEach(f -> {
scope.fork(() -> f.getTemperature(ville));
}
);
scope.join();
resultat = scope.result();
}
return resultat;
}
Qui renvoie le résultat de la première sous-tâche terminée
Et termine les autres sous-tâches restantes
Structured Concurrency (Second Preview)
Sciam
Possibilité de créer son propre scope
En héritant de la classe StructuredTaskScope
Et en y implémentant ses propres règles métiers
27
class ComposantLePlusLegerScope extends StructuredTaskScope<Composant> {
private final Collection<Composant> composants = new ConcurrentLinkedQueue<>();
private final Collection<Throwable> exceptions = new ConcurrentLinkedQueue<>();
@Override
protected void handleComplete(Subtask<? extends Composant> subtask) {
switch (subtask.state()) {
case SUCCESS -> this.composants.add(subtask.get());
case FAILED -> this.exceptions.add(subtask.exception());
case UNAVAILABLE -> {}
}
}
Structured Concurrency (Second Preview)
Sciam 28
public Exception exceptions() {
RuntimeException exception = new RuntimeException("Impossible d'obtenir le composant le plus leger");
exceptions.forEach(exception::addSuppressed);
return exception;
}
public Composant getComposant() throws Exception {
return composants.stream().min(Comparator.comparing(Composant::poids))
.orElseThrow(this::exceptions);
}
}
Structured Concurrency (Second Preview)
Sciam
Scoped Values (Second Preview)
Pour partager des objets dans le code exécuté par un thread
Historiquement depuis Java 1.2, on utilise une variable de type ThreadLocal
L’API ScopedValue tente de remédier à ces inconvénients
29
Mais cela présente plusieurs risques :
Mutable, fuite de mémoire, consommation de ressources
public final static ScopedValue<String> VALEUR = ScopedValue.newInstance();
Création d’une instance généralement statique et publique
Stocker et de partager des données immuables
Pour une durée de vie limitée à des traitements du thread qui les a écrits
En incubation dans Java 20 (JEP 429)
En preview dans Java 21 (JEP 446) et Java 22 sans modification
Sciam
Scoped Values (Second Preview)
where() pour définir une valeur, chainable pour plusieurs valeurs
get() pour obtenir la valeur ou lève une NoSuchElementException
30
Pour exécuter une tâche dans le thread courant
System.out.println((VALEUR.isBound() ? VALEUR.get() : "non definie"));
isBound() pour savoir si une valeur est associée au thread
run() : sous la forme d’une implémentation de Runnable
ScopedValue.where(VALEUR, "test").run(() -> { afficherValeur(); });
Ou call() : sous la forme d’une implémentation de Callable
String valeur = ScopedValue.where(VALEUR, "test")
.<String>call(monService::traiter);
Sciam
Scoped Values (Second Preview)
Réassociation d’une valeur pour un traitement sous-jacent
Partage avec les threads virtuels d’une StucturedTaskScope
31
ScopedValue.where(VALEUR, "valeur", () -> {
try (var scope = new StructuredTaskScope<String>()) {
afficherValeur();
scope.fork(monServiceA::traiter);
scope.fork(monServiceB::traiter);
scope.joinUntil(Instant.now().plusSeconds(10));
} catch (InterruptedException | TimeoutException e) {
e.printStackTrace();
}
});
ScopedValue.where(VALEUR, "valeur").run(() -> {
afficherValeur(); // valeur
ScopedValue.where(VALEUR, "autre-valeur").run(monService::traiter); // autre-valeur
afficherValeur(); // valeur
});
Sciam
Les fonctionnalités
du projet Panama
Foreign Function & Memory API
Vector API (Seventh Incubator)
Sciam
Foreign Function & Memory API
API de bas niveau pour de manière simple, sûre et efficace :
• Accéder à des données en mémoire hors du tas (off heap memory)
• Invoquer des fonctions natives
Proposée en preview en Java 19 (JEP 424), 20 (JEP 434) et 21 (JEP 442)
Elle est dans le package java.lang.foreign du module java.base
33
Historiquement, fusion de 2 JEPs introduites en incubation :
Foreign-Memory Access API en Java 14 (JEP 370, 383, et 393)
Et Foreign Linker API en Java 16 (JEP 389)
Proposée en incubation en Java 17 (JEP 412) et Java 18 (JEP 419)
En Standard en Java 22
Sciam
L’API de bas niveau
2) Pour invoquer du code natif
Une alternative à l’API JNI présente depuis Java 1.1
34
1) Pour accéder à des données
En mémoire hors du tas (off heap memory)
De manière sûre et performante
Alternative à certaines fonctionnalités
De java.nio.ByteBuffer (pas performante mais sûre)
Et sun.misc.Unsafe (non standard)
Foreign Function & Memory API
Requière une bonne connaissance des mécanismes natifs
De bas niveau utilisés (adresses, segments, formats des données,…)
Sciam
API dans le package java.lang.foreign du module java.base
En utilisant des fonctionnalités d’Invoke Dynamique
VarHandle pour l’accès accès aux données
MethodHandle pour l’invocation de méthodes
35
Définit des classes et des interfaces permettant :
De contrôler l'allocation et la désallocation de la mémoire étrangère :
MemorySegment, SegmentAllocator, Arena
Foreign Function & Memory API
D’appeler des fonctions étrangères :
SymbolLookup, FunctionDescriptor, Linker
De manipuler et accéder à la mémoire étrangère structurée :
MemoryLayout et dérivées
Autoriser les appels natifs --enable-native-access=ALL-UNNAMED
Pour éviter un warning et dans le futur une erreur pour des raisons de sécurité
Sciam
Foreign Function & Memory API
Contrôle de l'allocation et la désallocation de la mémoire étrangère
36
L’interface Arena qui hérite de SegmentAllocator
Gère l’accès à la mémoire native et leur isolation dans une portée
Et s’assure que les segments alloués seront libérés et qu’il n’y a pas d’accès après libération
global arena : une seule, partagée par tous les threads,
Libération des segments à l’arrêt de la JVM
Arena arena = Arena.global();
automatic arena : gérée par le ramasse-miettes,
Utilisable par plusieurs threads,
Libération des segments lorsqu’il n’y a plus de référence
Arena arena = Arena.ofAuto();
confined arena : les segments sont libérés à la fermeture
De l’arena, utilisable que par le thread qui l’a créé
try (Arena arena = Arena.ofConfined()) { . . . }
shared arena : combine un confined arena
Avec une utilisation multi-threads
Arena arena = Arena.ofShared();
4 Arena proposées :
Sciam
Foreign Function & Memory API
MemorySegment décrit une zone de mémoire off heap
Arena::allocateFrom(String)
Helper qui alloue un segment pour une chaîne en UTF-8 au format C
MemorySegment chaineNative = arena.allocateFrom("Java");
Arena::allocate(MemoryLayout)
Pour allouer un segment correspondant à une structure
Arena::allocate(long)
Alloue un segment dont la taille en octets est précisée
Allocation avec surcharges de méthodes de SegmentAllocator
MemorySegment segment = arena.allocate(100);
Notamment :
37
Sciam
Foreign Function & Memory API
Manipulation et accès à la mémoire étrangère structurée
38
MemoryLayout définit la structure de mémoire d'un type spécifique
ValueLayout hérite de MemoryLayout
Définit la façon dont les types de base sont stockés en mémoire
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(10 * 4); // allouer pour 10 entiers de type int (4 octets)
for (int i = 0 ; i < 10 ; i++) {
segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
}
}
Exemple :
ValueLayout.JAVA_LONG décrit le type Java primitif long
ValueLayout.ADDRESS décrit une adresse mémoire du matériel sous-jacent
Sciam
Foreign Function & Memory API
SequenceLayout hérite de MemoryLayout
Décrit un tableau d'un type spécifique décrit par un MemoryLayout
39
StructLayout hérite de MemoryLayout
Décrit une structure de types qui se suivent en mémoire
StructLayout dateStruct = MemoryLayout.structLayout(
ValueLayout.JAVA_SHORT.withName("annee"),
ValueLayout.JAVA_SHORT.withName("mois"),
ValueLayout.JAVA_SHORT.withName("jour"));
// tableau de 10 doubles Java
MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_DOUBLE);
// un tableau composé de trois tableaux de dix tableaux entiers
MemoryLayout.sequenceLayout(3, MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT));
Sciam
Foreign Function & Memory API
Manipulation de bas niveau d’un segment
40
SequenceLayout pointsLayout = MemoryLayout.sequenceLayout(10,
MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y")));
VarHandle xHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x"));
VarHandle yHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y"));
MemorySegment segment = arena.allocate(pointsLayout);
for (int i = 0; i < pointsLayout.elementCount(); i++) {
xHandle.set(segment, (long) i, i);
yHandle.set(segment, (long) i, i);
}
MemorySegment segment = arena.allocate((long)(2 * 4 * 10), 1);
for (int i = 0; i < 10; i++) {
segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2), i); // x
segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2) + 1, i); // y
}
Manipulation d’un segment structuré avec des VarHandles
Sciam
Foreign Function & Memory API
Recherche et description de fonctions étrangères
41
L’interface SymbolLookup
Pour localiser et rechercher l’adresse de fonctions dans les bibliothèques natives
L’interface FunctionDescriptor
Décrit les paramètres entrants et sortants d'une fonction native
Chargement optionnel de la bibliothèque native par le ClassLoader
Avec System::loadLibrary
Des fabriques pour obtenir une instance :
static SymbolLookup loaderLookup()
pour une instance sur les bibliothèques chargées par le ClassLoader
static SymbolLookup libraryLookup(String, Arena) et libraryLookup(Path, Arena)
pour une instance sur une bibliothèque qui sera chargée au besoin
Lors de l'appel de fonction native avec un MethodHandle
s'assure que les types Java transférés sont convertis en types C corrects
et que la valeur de retour est convertie d'un type C vers le type de retour Java
Sciam
Foreign Function & Memory API
Obtenir un éditeur de liens spécifique à l’Application Binary Interface
42
Linker
Crée un éditeur de liens spécifique à l’ABI de la plate-forme native sous-jacente
Linker linker = Linker.nativeLinker();
Une ABI est un ensemble de conventions d’appel et de types de données
Associés au compilateur, au système d’exploitation et au processeur utilisés
Optimisé pour les conventions d'appel de nombreuses plateformes :
Linux/x64 Linux/AArch64 Linux/RISC-V Linux/PPC64 Linux/s390
macOS/x64 macOS/AArch64 Windows/x64 Windows/AArch64 AIX/ppc64
Prend en charge les conventions d'appel des autres plateformes en déléguant à libffi
SymbolLookup stdlib = linker.defaultLookup();
La méthode defaultLookup() renvoie un SymbolLookup
Pour rechercher une fonction dans un ensemble de bibliothèques courantes
Exemple : libc sous Linux
Sciam
Foreign Function & Memory API
Appel à des fonctions étrangères via des MethodHandles
43
Linker permet une interaction aller-retour
Entre le code Java et les fonctions étrangères
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").get(), FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
try (Arena arena = Arena.ofConfined()) {
MemorySegment chaine = arena.allocateFrom("Java");
long len = (long) strlen.invoke(chaine);
}
Les appels ascendants (upcall)
Appels du code natif vers le code Java
Les appels descendants (downcall)
Appels du code Java vers le code natif
Sciam
public class DialogFFM {
public static void main(String[] args) {
try {
System.loadLibrary("user32");
Optional<MemorySegment> msgBoxFunction = SymbolLookup.loaderLookup().find("MessageBoxA");
FunctionDescriptor msgBoxFunctionDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, ADDRESS, JAVA_INT);
Linker linker = Linker.nativeLinker();
MethodHandle methodHandle = linker.downcallHandle(msgBoxFunction.get(), msgBoxFunctionDesc);
try (Arena offHeap = Arena.ofConfined()) {
MemorySegment cStringMessage = offHeap.allocateFrom("Voulez-vous utiliser Java 22 ?");
MemorySegment cStringTitre = offHeap.allocateFrom("Confirmation");
int bouton = (int) methodHandle.invoke(NULL, cStringMessage, cStringTitre, 36);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
44
Foreign Function & Memory API
Exemple sous Windows
Sciam
Vector API (Seventh Incubator)
Exprimer des calculs vectoriels
Qui, au moment de l'exécution, sont systématiquement compilés
Avec les meilleures instructions vectorielles possibles sur l’architectures CPU
Les SIMD sur les CPU supportées : x64 (SSE et AVX) et AArch64 (Neon)
Dans le module jdk.incubator.vector
45
Single Instruction, Multiple Data
Traiter en parallèle un tableau de données, sans threads
Pour appliquer une même opération sur plusieurs valeurs traitées
En un seul cycle de traitement CPU
En incubation en Java 16 (JEP 338), 17 (JEP 414), 18 (JEP 417),
19 (JEP 426), 20 (JEP 438) et 21 (JEP 448)
Sciam
static float[] calculerScalaire(float[] a, float[] b) {
float[] c = new float[a.length];
for (int i = 0 ; i < a.length ; i++) {
c[i] = a[i] * a[i] - b[i] * b[i];
}
return c;
}
46
API plutôt de bas niveau, verbeuse, dépendant partiellement du CPU
Mais qui peut offrir de meilleures performances que le code scalaire équivalent
Exemple Java 22
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
static float[] calculerVectoriel(float[] a, float[] b) {
float[] c = new float[a.length];
int i = 0;
for (; i < SPECIES.loopBound(a.length) ; i += SPECIES.length()) {
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vr = va.mul(va).sub(vb.mul(vb));
vr.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = a[i] * a[i] - b[i] * b[i];
}
return c;
}
Vector API (Seventh Incubator)
Sciam
Les évolutions
dans les API de Java Core
Stream Gatherers (Preview)
Class-File API (Preview)
Sciam
Stream Gatherers (Preview)
L'API Stream fournit un ensemble complet mais fixe d'opérations
Intermédiaires et terminales : filtrage, transformation, réduction, tri, …
48
Le but : proposer l’opération intermédiaire Stream::gather extensible
Qui permet d’exprimer quasiment toutes les implémentations voulues
En utilisant l’API Gatherer similaire à l’API Collector
Une opération terminale extensible Stream::collect
Qui permet de produire le résultat d'un pipeline de diverses manières
En utilisant l’API Collector
Mais ce nombre fixe d’opérations empêche d’exprimer
Certains traitements complexes
Plusieurs opérations intermédiaires ont déjà été ajoutées
Mais ce n’est pas solution maintenable dans le temps
Sciam
Stream Gatherers (Preview)
Attend en paramètre une implémentation de l’interface Gatherer
49
Elle définit quatre fonctions :
default BiConsumer<A,Gatherer.Downstream<? super R>> finisher()
Optionnelle, invoquée lorsqu'il n'y a plus d'éléments à traiter
Peut utiliser l'objet d'état privé
Pour éventuellement, émettre des éléments de sortie supplémentaires
Gatherer.Integrator<A,T,R> integrator()
Intègre un nouvel élément du flux d'entrée éventuellement avec l'objet d'état privé
Pour émettre éventuellement des éléments vers le flux de sortie
Peut interrompre le traitement avant d'atteindre la fin du flux d'entrée
default Supplier<A>initializer()
Fonction d'initialisation facultative fournit un objet
Qui conserve un état privé pendant le traitement des éléments du flux
default BinaryOperator<A> combiner()
Optionnelle, utilisée pour combiner les gatherers
Lorsque le flux d'entrée est marqué comme parallèle
Sciam
Stream Gatherers (Preview)
L’interface fonctionnelle Gatherer.Downstream<T>
50
La méthode abstraite boolean push(T element)
Pour envoyer éventuellement l’élément en sortie
Elle renvoie un booléen : false pour short cirtuiting sinon true
L’interface fonctionnelle Gatherer.Integrator<A, T, R>
Une instance est retournée par l’integrator
Une instance est fournie à l’integrator et au finisher
La méthode boolean integrate(A state, T element, Downstream<? super R> downstream)
Pour traiter un élément entrant et renvoyer un booléen (false pour short cirtuiting)
Function<Personne, String> mapper = // . . . Transforme une Personne en String
Integrator<Void, Personne, String> integrator = (state, element, downstream) -> {
R mappedElement = mapper.apply(element);
downstream.push(mappedElement);
return true;
};
Sciam
Stream Gatherers (Preview)
L’interface Gatherer propose aussi différentes surcharges de la fabrique of()
Pour obtenir un Gatherer
À partir de l’implémentation d’une ou plusieurs des quatre fonctions
51
Exemple : un Gatherer équivalent à l’opération map()
public static <T, R> Gatherer<T, Void, R> mapping(Function<? super T, ? super R> mapper) {
return Gatherer.of( (_ , element, downstream) -> {
R mapped = mapper.apply(element);
downstream.push(mapped);
return true;
});
}
Un Gatherer peut être :
Exécuté en séquentiel ou parallèle
Stateless ou statefull
Short-Circuiting ou greedy
Sciam
Stream Gatherers (Preview)
La classe Gatherers propose des fabriques pour usages courants
52
fold(Supplier<R> initial, BiFunction<? super R,? super T,? extends R> folder)
Renvoie un Gatherer qui construit un agrégat de manière incrémentielle
Et émet cet agrégat lorsqu'il n'y a plus d'éléments d'entrée
scan(Supplier<R> initial, BiFunction<? super R,? super T,? extends R> scanner)
Renvoie un Gatherer 1-1 qui applique une fonction fournie
À l'état actuel et à l'élément pour produire l'élément suivant, qu'il transmet en sortie
List<String> nombreStr = Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.fold(() -> "", (string, number) -> !string.isEmpty() ? string + ";" + number : string + number))
.toList();
System.out.println(nombreStr); // [1;2;3;4;5]
List<String> nombreStrs = Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.scan(() -> "", (string, number) -> string + number))
.toList();
System.out.println(nombreStrs); // [1, 12, 123, 1234, 12345]
Sciam
Stream Gatherers (Preview)
windowFixed(int windowSize)
Renvoie un Gatherer n-m qui regroupe les éléments d'entrée
Dans des listes d'une taille donnée
Et transmet les listes en sortie lorsqu'elles sont pleines
53
windowSliding(int windowSize)
Renvoie un Gatherer n-m qui regroupe les éléments d'entrée
Dans des listes d'une taille fournie
Après la première fenêtre, chaque liste suivante est créée à partir d'une copie de
La précédente en supprimant le premier élément
Et en ajoutant l'élément suivant à partir du flux d'entrée
List<List<Integer>> windowsFixed = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).gather(Gatherers.windowFixed(3)).toList();
System.out.println(windowsFixed); // [[1, 2, 3], [4, 5, 6], [7, 8]]
List<List<Integer>> windowSlicing = Stream.of(1, 2, 3, 4, 5).gather(Gatherers.windowSliding(3)).toList();
System.out.println(windowSlicing); // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
Sciam
Stream Gatherers (Preview)
mapConcurrent(int maxConc, Function<? super T,? extends R> mapper)
Renvoie un Gatherer 1-1 qui invoque une fonction fournie
Sur chaque élément d'entrée en parallèle, jusqu'à une limite fournie
54
Sciam
Stream Gatherers (Preview)
Les gatherers supportent la composition
55
via la méthode andThen(Gatherer)
Qui joint deux gatherers
Où le premier produit des éléments que le second peut consommer
Cela permet de créer des gatherers sophistiqués
En composant des gatherers plus simples
Tout comme la composition de fonctions
Ainsi sémantiquement :
source.gather(a).gather(b).gather(c).collect(...)
Est équivalent à :
source.gather(a.andThen(b).andThen(c)).collect(...)
Sciam
Class-File API (Preview)
L'écosystème Java dispose de plusieurs bibliothèques
Pour manipuler le bytecode
Toutes hors du JDK : ASM, BCEL, Javassist, ByteBuddy, …
56
But : fournir une API standard
Pour l'analyse, la génération et la transformation de fichier de classe Java
Le JDK utilise en interne ASM
Dans une version N-1 par rapport à la version N du JDK
Dans le package java.lang.classfile
Utilisées par de nombreux frameworks
Sciam
Class-File API (Preview)
API riche pour permettre un accès random ou séquentiel
57
La génération utilise des builders fournis en paramètre d’interfaces fonctionnelles
Les différents composants sont modélisés :
xxxModel représentent des structures complexes, immuables
telles que des classes, des méthodes, des champs, le corps d'une méthode
xxxElement représentent un élément du fichier de classe, immuables
Les Elements peuvent être des Models et un Model possède un Element correspondant
xxxEntry représentent les éléments du constant pool (PoolEntry, ClassEntry, Utf8Entry)
Également exposés sous la forme de Model et d’Element
Attribute représentent un attribut d’un élément
Majoritairement exposés sous la forme d’Element
API moderne
Utilisation de fabriques, de types scellés, d’immutabilité, …
Utilise les types du package java.lang.constant pour les informations symboliques
Sciam
Class-File API (Preview)
La génération de fichiers de classe
58
import java.lang.classfile.ClassFile;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.file.Path;
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
import static java.lang.classfile.ClassFile.ACC_STATIC;
import static java.lang.constant.ConstantDescs.CD_int;
import static java.lang.constant.ConstantDescs.CD_long;
public class TestClassFile {
public static void main(String[] args) throws java.io.IOException {
ClassFile.of().buildTo(Path.of("EntierUtils.class"),
ClassDesc.of("EntierUtils"),
classBuilder -> classBuilder.withMethodBody("ajouter",
MethodTypeDesc.of(CD_long, CD_int, CD_int),
ACC_PUBLIC | ACC_STATIC,
codeBuilder -> codeBuilder.iload(1)
.i2l()
.iload(2)
.i2l()
.ladd()
.lreturn()));
}
}
C:javaTestJava22> javap -c .EntierUtils.class
public class EntierUtils {
public static long ajouter(int, int);
Code:
0: iload_1
1: i2l
2: iload_2
3: i2l
4: ladd
5: lreturn
}
Equivalent à (sans constructeur par défaut)
public class EntierUtils {
public static long ajouter(int a, int b) {
return (long) a + b;
}
}
Sciam
Class-File API (Preview)
La lecture et l’analyse des fichiers de classe
Avec plusieurs formes de parcours proposées
59
ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class"));
for (ClassElement ce : cm) {
switch (ce) {
case MethodModel mm -> {
System.out.printf("Methode %s%s%n", mm.methodName().stringValue(), mm.methodTypeSymbol().displayDescriptor());
for (MethodElement me : mm)
if (me instanceof CodeModel xm)
for (CodeElement el : xm)
System.out.printf(" %s%n", el);
}
default -> {}
}
}
Methode ajouter(int,int)long
Load[OP=ILOAD_1, slot=1]
UnboundConvertInstruction[op=I2L]
Load[OP=ILOAD_2, slot=2]
UnboundConvertInstruction[op=I2L]
UnboundOperatorInstruction[op=LADD]
Return[OP=LRETURN]
Débute par l’obtention d’un ClassModel
En invoquant une surcharge de ClassFile::parse
Parcours des éléments encapsulés dans un type ClassElement
Car ClassModel implémente Iterable<ClassElement>
Sciam
Class-File API (Preview)
Parcours des éléments avec un Stream
60
ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class"));
for (MethodModel mm : cm.methods()) {
System.out.printf("Methode %s%s%n", mm.methodName().stringValue(), mm.methodTypeSymbol().displayDescriptor());
for (MethodElement me : mm)
if (me instanceof CodeModel xm)
for (CodeElement el : xm)
System.out.printf(" %s%n", el);
}
Parcours des éléments selon leur type
En invoquant des méthodes dédiées : fields(), methods()
ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class"));
cm.elementStream()
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
.flatMap(me -> me instanceof CodeModel com ? com.elementStream() : Stream.empty())
.forEach(com -> System.out.printf(" %s%n", com));
Sciam
Class-File API (Preview)
La transformation de fichiers de classe
61
Souvent la combinaison de lecture et écriture
Où un fichier de classe est lu, des modifications localisées sont apportées,
Mais une grande partie du fichier de classe est transmise sans modification
Chaque XxxBuilder dispose d'une méthode with(XxxElement)
Qui permet d’inclure directement les éléments sans les modifier
ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class"));
byte[] newBytes = ClassFile.of().build(cm.thisClass().asSymbol(),
classBuilder -> {
for (ClassElement ce : cm) {
if (!(ce instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter"))) {
classBuilder.with(ce);
}
}
});
Exemple : retirer la méthode ajouter()
Sciam
Class-File API (Preview)
Une transformation peut être considérée comme une « flatMap »
Sur la séquence d'éléments
62
ClassTransform ct = (builder, element) -> {
if (!(element instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter")))
builder.with(element);
};
var cc = ClassFile.of();
byte[] newBytes = cc.transform(cc.parse(Path.of("EntierUtils.class")), ct);
Chaque Model a un type Transform correspondant
ClassTransform::dropping helper pour retirer des éléments
ClassTransform ct = ClassTransform.dropping(
element -> element instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter"));
Sciam
Class-File API (Preview)
Combinaison de transformations
63
Exemple : remplacer tous les opcodes ladd par lsub
Pour faire manuellement une transformation pour du mutation testing
CodeTransform laddToLsub = (b, e) -> {
if (e instanceof Instruction i && i.opcode() == Opcode.LADD) b.lsub();
else b.with(e);
};
Définition d’un CodeTransform
MethodTransform mt = MethodTransform.transformingCode(laddToLsub);
ClassTransform ct = ClassTransform.transformingMethods(mt);
Définition d’un MethodTransform utilisant le CodeTransform et d’un ClassTransform
ClassTransform ct = ClassTransform.transformingMethodBodies(laddToLsub);
Ou plus simplement
var cc = ClassFile.of();
byte[] newBytes = ClassFile.of().transform(cc.parse(Path.of("EntierUtils.class")), ct);
Application de la transformation
Sciam
La classe ListFormat
Pour formater ou analyser une liste de chaîne de caractères
D’une manière sensible aux paramètres régionaux (Locale)
64
Le type de mise en forme avec l’énumération ListFormat.Type
Qui détermine la ponctuation entre les chaînes et les mots de liaison, le cas échéant
Valeurs définies : STANDARD (par défaut), OR et UNIT
var elements = List.of("E1", "E2", "E3");
System.out.println(ListFormat.getInstance().format(elements));
System.out.println(ListFormat.getInstance(Locale.FRANCE, OR, FULL).format(elements));
System.out.println(ListFormat.getInstance(Locale.US, STANDARD, FULL).format(elements));
System.out.println(ListFormat.getInstance(Locale.US, STANDARD, SHORT).format(elements));
E1, E2 et E3
E1, E2 ou E3
E1, E2, and E3
E1, E2, & E3
Une instance est obtenue avec une fabrique à qui on peut passer :
Une locale, sinon la locale par défaut sera utilisée
Le style de mise en forme avec l’énumération ListFormat.Style
Qui adapte la façon dont les chaînes sont abrégées (ou non) selon le type
Valeurs définies : FULL (par défaut), SHORT et NARROW
Sciam
Ajout divers dans l’API Java Core
boolean Console.isTerminal()
Retourne un booléen qui indique si l’instance de la console est un terminal
65
static class<?> Class.forPrimitiveName(String)
Retourne la classe associée au type primitif donné sinon null
Les 9 types primitifs sont boolean, byte, char, short, int, long, float, double et void
static InetAddress InetAddress.ofLiteral(String)
Créé une instance depuis la représentation textuelle de l’adresse IP
Existe aussi pour les classes Inet4Address et Inet6Address
Default DoubleStream RandomGenerator.equiDoubles(double left,
double right, boolean isLeftIncluded, boolean isRightIncluded)
Renvoie un Stream illimité de valeurs doubles choisies de manière pseudo-aléatoire,
Où chaque valeur est comprise entre la limite gauche et la limite droite spécifiées
Incluses ou non avec une garantit une distribution uniforme
Sciam
Les évolutions
dans la JVM HotSpot
Launch Multi-File Source-Code Programs
Region Pinning for G1
Sciam
Launch Multi-File Source-Code Programs
Depuis le JDK 11, la JVM peut exécuter UN unique fichier .java
Sans avoir à le compiler en préalable ( JEP 330 )
Pouvant contenir plusieurs types
67
Exécuter la JVM avec le fichier .java
Une compilation en mémoire est effectuée avant l’exécution
public class Hello {
public static void main(String[] args) {
Utils.saluer();
}
interface Utils {
static void saluer() {
System.out.println("Hello");
}
}
}
C:java>java Hello.java
Hello
C:java>dir
14/02/2024 17:40 <DIR> .
16/01/2024 10:14 <DIR> ..
14/02/2024 17:40 195 Hello.java
Sciam
Launch Multi-File Source-Code Programs
But : permettre l’exécution de code dans plusieurs fichiers source
68
Impossible d'avoir un même type déclaré dans plusieurs fichiers source
Pas de garantie d’ordre ou de temporalité pour la compilation .java
Compilation à la volée ou lazy
Si une classe utilisée est dans le même fichier .java
Elle n’est pas recherchée et est compilée et utilisée
Seuls les .java dont les types sont utilisés par l’application sont compilés
public class Hello {
public static void main(String[] args) {
Utils.saluer();
}
}
public interface Utils {
static void saluer() {
System.out.println("Hello");
}
}
C:java>java Hello.java
Hello
C:java>dir /W
[.] [..]
Hello.java Utils.java
Sciam
Region Pinning for G1
JNI a besoin de définir et libérer des pointeurs sur des objets Java
Les traitements sont lors exécutés dans une section dite critique
Ces objets ne doivent pas être déplacés par le GC
69
But : Réduire la latence en permettant l'épinglage des régions par G1
Contenant un objet d’une section critique de JNI
Historiquement, G1 suspend tous ses traitements
Lors de l’exécution de régions critiques de JNI
Pouvant impliquer un risque de bloquer les threads de l’application
Voir même une OutOfMemoryError
Les régions sans section critique peuvent être traitées
Évitant de bloquer les autres threads de l’application pouvant nécessiter le GC
Améliore les performances et réduit les risques avec JNI
Sciam
Les événements JFR
Plusieurs événements JFR ajoutés dans la JVM HotSpot
70
CompilerQueueUtilization ( JDK-8317562 )
Statistiques des queues des compilateur du JIT
Un événement pour C1 et C2
DeprecatedInvocation ( JDK-8211238 )
Invocation unique d’une méthode annotée avec @Deprecated du JDK
NativeLibraryLoad ( JDK-8313251 ) et NativeLibraryUnLoad ( JDK-8314211)
Informations sur une opération de chargement / déchargement
D’une bibliothèque dynamique ou une image native
Sciam
Les autres évolutions dans les GC
Plusieurs améliorations ont été apportées dans les GC
71
G1 GC
Code root scan causes long GC pauses due to imbalanced iteration (JDK-8315503)
Fast Collection of Evacuation Failed Regions (JDK-8140326)
More Deterministic Heap Resize at Remark (JDK-8314573)
Parallel GC
amélioration du throughput avec des grands tableaux (JDK-8321013)
Precise Parallel Scanning of Large Object Arrays for Young Collection Roots (JDK-8310031)
Serial GC
Better GC Throughput with Scarce Dirty Cards (JDK-8319373)
Sciam
Les évolutions
dans les outils du JDK
Les évolutions dans javac
Sciam
Avant le JDK 22, le compilateur javac acceptait
les références de méthode privées avec comme récepteur une variable de type
73
import java.util.function.*;
class Fonction {
private String asString() { return "test"; }
static <T extends Fonction> Function<T, String> get() {
return T::asString;
}
}
Avec le JDK 22, le compilateur javac émet une erreur
C:java>javac Fonction.java
Fonction.java:9: error: asString() has private access in Fonction
return T::asString;
^
1 error
Les évolutions dans javac
Sciam
Avant le JDK 22, le compilateur javac permettait
l’utilisation du mot clé final au début d’un record pattern
dans le case d’un switch utilisant du pattern matching
74
Object o = new Pays("France");
switch ( o ) {
case final Pays(var n) -> {}
default -> {}
}
Avec le JDK 22, le compilateur javac émet une erreur
C:java>javac Pays.java
Pays.java:6: error: modifier final not allowed here
case final Pays(var n) -> {}
^
1 error
Les évolutions dans javac
Sciam
Conclusion
Sciam
Conclusion
Java poursuit son évolution en respectant son modèle de releases
La syntaxe (Amber)
76
Java 22 proposent des fonctionnalités concernant :
Cela permettra à Java de rester pertinent aujourd’hui et demain
La programmation parallèle et concurrente (Loom)
Une meilleure utilisation du matériel moderne (Panama) avec FFM en standard
N’hésitez pas à télécharger un JDK 22 auprès d’un fournisseur
L’API Stream Gatherers et ClassFile en preview
Sciam
Merci pour votre attention
Sciam

Contenu connexe

Similaire à Les nouveautés de Java 22 - Lorraine JUG avril 2024.pdf

Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdfLes nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
Jean-Michel Doudoux
 
Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5
Mamadou Oury Ba
 
Présentation nouveauté java7
Présentation nouveauté java7Présentation nouveauté java7
Présentation nouveauté java7
Cynapsys It Hotspot
 
Java 5, un blian
Java 5, un blianJava 5, un blian
Java 5, un blian
tareq
 
Java 5, un bilan
Java 5,  un bilanJava 5,  un bilan
Java 5, un bilan
teejug
 
L’environnement de programmation fonctionnelle DrRacket
L’environnement de programmation fonctionnelle DrRacketL’environnement de programmation fonctionnelle DrRacket
L’environnement de programmation fonctionnelle DrRacket
Stéphane Legrand
 
Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017
Jean-Michel Doudoux
 
Cours de C++, en français, 2002 - Cours 3.4
Cours de C++, en français, 2002 - Cours 3.4Cours de C++, en français, 2002 - Cours 3.4
Cours de C++, en français, 2002 - Cours 3.4
Laurent BUNIET
 
Visual Studio 2008 Overview
Visual Studio 2008 OverviewVisual Studio 2008 Overview
Visual Studio 2008 OverviewGregory Renard
 
Cours java smi 2007 2008
Cours java smi 2007 2008Cours java smi 2007 2008
Cours java smi 2007 2008Khalil Lechheb
 
Cours java smi 2007 2008
Cours java smi 2007 2008Cours java smi 2007 2008
Cours java smi 2007 2008Khalil Lechheb
 
Introduction java
Introduction javaIntroduction java
Introduction java
Fouad Root
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8lyonjug
 
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
Paris Salesforce Developer Group
 
Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016
Jean-Michel Doudoux
 
C# 7 - Nouveautés
C# 7 - NouveautésC# 7 - Nouveautés
C# 7 - Nouveautés
James RAVAILLE
 
Change mind about JS
Change mind about JSChange mind about JS
Change mind about JS
Damien Simonin Feugas
 
Dotnet csharp
Dotnet csharpDotnet csharp
Dotnet csharp
SDFG5
 
System c eniso_jan_fev_07
System c eniso_jan_fev_07System c eniso_jan_fev_07
System c eniso_jan_fev_07
haythem_2015
 

Similaire à Les nouveautés de Java 22 - Lorraine JUG avril 2024.pdf (20)

Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdfLes nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
Les nouveautés de Java 21 - Lorraing JUG mars 2024.pdf
 
Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5
 
Présentation nouveauté java7
Présentation nouveauté java7Présentation nouveauté java7
Présentation nouveauté java7
 
Java 5, un blian
Java 5, un blianJava 5, un blian
Java 5, un blian
 
Java 5, un bilan
Java 5,  un bilanJava 5,  un bilan
Java 5, un bilan
 
L’environnement de programmation fonctionnelle DrRacket
L’environnement de programmation fonctionnelle DrRacketL’environnement de programmation fonctionnelle DrRacket
L’environnement de programmation fonctionnelle DrRacket
 
Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017
 
Cours de C++, en français, 2002 - Cours 3.4
Cours de C++, en français, 2002 - Cours 3.4Cours de C++, en français, 2002 - Cours 3.4
Cours de C++, en français, 2002 - Cours 3.4
 
Visual Studio 2008 Overview
Visual Studio 2008 OverviewVisual Studio 2008 Overview
Visual Studio 2008 Overview
 
Cours java smi 2007 2008
Cours java smi 2007 2008Cours java smi 2007 2008
Cours java smi 2007 2008
 
Cours java smi 2007 2008
Cours java smi 2007 2008Cours java smi 2007 2008
Cours java smi 2007 2008
 
Introduction java
Introduction javaIntroduction java
Introduction java
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8
 
Jdbc
JdbcJdbc
Jdbc
 
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
La Tooling API, est-ce pour moi ? Bien sûr, viens voir pourquoi !
 
Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016
 
C# 7 - Nouveautés
C# 7 - NouveautésC# 7 - Nouveautés
C# 7 - Nouveautés
 
Change mind about JS
Change mind about JSChange mind about JS
Change mind about JS
 
Dotnet csharp
Dotnet csharpDotnet csharp
Dotnet csharp
 
System c eniso_jan_fev_07
System c eniso_jan_fev_07System c eniso_jan_fev_07
System c eniso_jan_fev_07
 

Plus de Jean-Michel Doudoux

Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Jean-Michel Doudoux
 
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDKVoxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Jean-Michel Doudoux
 
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdfdevoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
Jean-Michel Doudoux
 
Lyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenantLyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenant
Jean-Michel Doudoux
 
Nantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenantNantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenant
Jean-Michel Doudoux
 
Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10
Jean-Michel Doudoux
 
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMXAnniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
Jean-Michel Doudoux
 
Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10
Jean-Michel Doudoux
 
Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018
Jean-Michel Doudoux
 
Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8
Jean-Michel Doudoux
 
"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012
Jean-Michel Doudoux
 

Plus de Jean-Michel Doudoux (11)

Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17
 
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDKVoxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
 
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdfdevoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
 
Lyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenantLyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenant
 
Nantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenantNantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenant
 
Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10
 
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMXAnniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
 
Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10
 
Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018
 
Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8
 
"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012
 

Les nouveautés de Java 22 - Lorraine JUG avril 2024.pdf

  • 1. 1 les nouveautés de Java 22 @jmdoudoux JMdoudouX 8 avril 2024
  • 2. Sciam Java SE en 2024 2 Java poursuit son évolution En respectant le rythme de release tous les 6 mois JDK 21 est la version LTS courante Publiée le 19 septembre 2023 Depuis la réduction de 3 à 2 ans entre 2 LTS après Java 17 OpenJDK 21 est l’implémentation de référence Des spécifications de la version 21 de Java SE Des patchs seront disponibles Au moins jusqu’à la prochaine version LTS Le JDK 22 a été le 19 mars 2024
  • 3. Sciam Java 22 Les spécifications dans la JSR 397 3 12 JEPs Les nouvelles fonctionnalités En Preview JEP 457 : Class-File API (Preview) JEP 461 : Stream Gatherers (Preview) JEP 447 : Statements before super(...) (Preview) Standard JEP 458 : Launch Multi-File Source-Code Programs JEP 423 : Region Pinning for G1 JEP 454 : Foreign Function & Memory API Les fonctionnalités qui sortent de preview JEP 456 : Unnamed Variables & Patterns Les fonctionnalités qui restent en preview ou incubation JEP 462 : Structured Concurrency (Second Preview) JEP 464 : Scoped Values (Second Preview) JEP 459 : String Templates (Second Preview) JEP 460 : Vector API (Seventh Incubator) JEP 463 : Implicitly Declared Classes and Instance Main Methods (Second Preview)
  • 4. Sciam 4 Jean-Michel Doudoux https://www.jmdoudoux.fr @jmdoudoux Co-fondateur du Auteur de 2 didacticiels Diffusés sous licence GNU FDL • Développons en Java (4200 pages) • Développons en Java avec Eclipse Senior Tech Lead chez
  • 5. Sciam Roadmap Les fonctionnalités du projet Amber 5 Les évolutions du projet Loom dans la JVM Hotspot dans les API Java Core du projet Panama dans les outils du JDK
  • 6. Sciam Les fonctionnalités du projet Amber Unnamed Variables & Patterns String Templates (Second Preview) Implicitly Declared Classes and Instance Main Methods (Second Preview) Statements before super(...) (Preview)
  • 7. Sciam Unnamed Variables & Patterns En preview en Java 21 (JEP 443), en standard en Java 22 7 Unnamed pattern : un pattern inconditionnel qui ne correspond à rien Utilisable dans un pattern imbriqué à la place d'un type ou record pattern record Grade(String code, String designation) {} record Employe(String nom, String prenom, Grade grade) {} Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur")); if (o instanceof Employe(var nom, var prenom, _)) { System.out.println("Employe : " + nom + " " + prenom); } En utilisant le 51eme mot clé réservé de Java : _ But : Enrichir le langage d’une syntaxe Pour les patterns inutilisés dans les records pattern imbriqués Et les variables inutilisées qui doivent être déclarées 3 patterns, utilisables plusieurs fois dans la même portée
  • 8. Sciam Unnamed Variables & Patterns Unnamed pattern variable : Utilisable avec tous types de patterns 8 Unnamed variable : peut être initialisée mais non utilisée dans Une variable locale dans un bloc Une ressource dans un try-with-resources L'en-tête d'une boucle for et for améliorée Une exception d'un bloc catch Un paramètre formel d'une expression Lambda try (var _ = ScopedContext.acquire()) { var _ = service.traiter((_, _) -> System.out.printn("traiter")); } catch (Throwable _) { } if (o instanceof Employe(var nom, var _, _)) { System.out.println("Employe : " + nom); }
  • 9. Sciam Unnamed Variables & Patterns Utile dans des switchs avec des patterns sur des types scellés 9 void traiterFormeRonde(Forme forme) { switch(forme) { case Cercle c -> afficher(c); case Carre c -> {} case Rectangle r -> {} } } switch(forme) { case Cercle c -> afficher(c); default -> {} } switch(forme) { case Cercle c -> afficher(c); case Carre _, Rectangle _ -> {} } Impossible d’avoir plusieurs patterns nommés dans un case Risque de bug en cas d’ajout d’un type : sealed interface Forme permits Cercle, Carre, Rectangle {} Utilisation possible de default préférable d’utiliser des unnamed variables
  • 10. Sciam String Templates (Second Preview) Courant de devoir créer des chaînes de caractères composées À partir d'une combinaison de textes littéraux Et de valeurs ou d'expressions 10 Historiquement plusieurs fonctionnalités, toutes avec inconvénients String s = x + " + " + y + " = " + (x + y); MessageFormat mf = new MessageFormat("{0} + {1} = {2}"); String s = mf.format(x, y, x + y); String s = String.format("%2$d + %1$d = %3$d", x, y, x + y); String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y); String s = new StringBuilder() .append(x) .append(" + ") .append(y) .append(" = ") .append(x + y) .toString(); De nombreux langages proposent l'interpolation de chaînes Comme alternative à la concaténation de chaînes Première preview en Java 21 via la JEP 430
  • 11. Sciam String Templates (Second Preview) La plupart des langages supportent l’interpolation de chaînes Mais le résultat peut parfois engendrer des soucis indirects Exemple : SQL ou JSON injection 11 En combinant : - Un texte littéral avec des expressions intégrées - Et un processeur de templates Pour produire des chaînes de caractères construites dynamiquement Avec la clarté de l’interpolation et un résultat plus sûr Le but : enrichir le langage Java avec des string templates Qui complètent les chaînes littérales et les blocs de texte Possibilité de créer une instance de type quelconque Avec un processeur de templates personnalisés
  • 12. Sciam String Templates (Second Preview) Nouveau type d'expression dans le langage : les templates expressions Pour effectuer une interpolation de chaîne pour créer une chaîne ou un objet 12 Syntaxiquement, ressemble à une chaîne littérale avec un préfixe : String prenom = "Jean-Michel"; String message = STR."Bonjour {prenom}"; Une template expression est composée de trois éléments : 1) Un processeur de templates (STR) 2) Un caractère point (U+002E), celui utilisé dans les autres expressions 3) Un template ("Bonjour {prenom}") qui contient une expression intégrée ({prenom}) Le template peut utiliser plusieurs lignes de code source En utilisant une syntaxe similaire à celle des blocs de texte
  • 13. Sciam String Templates (Second Preview) 3 processeurs de templates dans le JDK 13 STR : effectue une interpolation pour créer une chaîne int x = 10, y = 20; String s = STR."{x} + {y} = {x + y}"; FMT : effectue une interpolation pour créer une chaîne Il interprète les spécificateurs de format à gauche des expressions intégrées Les spécificateurs de format sont ceux définis dans java.util.Formatter RAW : produit un objet de type StringTemplate String prenom = "Jean-Michel"; StringTemplate st = RAW."Bonjour {prenom}"; String message = STR.process(st);
  • 14. Sciam String Templates (Second Preview) Possibilité de définir des processeurs de templates personnalisés Pour générer des chaînes ou des objets qui peuvent être validés 14 Une instance de l'interface fonctionnelle StringTemplate.Processor Implémenter l’unique méthode process() Utilisation de la fabrique StringTemplate.Processor::of Pour obtenir une instance var JSON = StringTemplate.Processor.of((StringTemplate st) -> new JSONObject(st.interpolate())); String nom = "Durant"; String prenom = "Pierre"; JSONObject doc = JSON.""" { "nom": "{nom}", "prenom": "{prenom}" }""";
  • 15. Sciam String Templates (prochaine Preview) Dans la JEP sur les fonctionnalités en preview Il est prévu qu’elle puisse évoluer (voire même être supprimée) En fonction des feed backs issus des previews 15 C’est le cas avec les String Templates Une annonce est publiée sur la mailing list Par Brian Goetz le 9 mars 2024 https://mail.openjdk.org/pipermail/amber-spec-experts/2024-March/004010.html La fonctionnalité va profondément changer Il va falloir attendre les détails dans la prochaine JEP dans le JDK 23 24
  • 16. Sciam Implicitly Declared Classes and Instance Main Methods (Second Preview) 16 Plutôt compliqué public class HelloWorld { public static void main(String[] args) { System.out.println("Hello world"); } } Les buts : Faire évoluer le langage pour simplifier les programmes simples Et faciliter l’apprentissage des débutants avec le langage Java Première preview dans le JDK 21 via la JEP 445 Sous le nom Unnamed Classes and Instance Main Methods (Preview) Changement de nom de la JEP dans la seconde preview Deux évolutions dans un fichier source
  • 17. Sciam Implicitly Declared Classes and Instance Main Methods (Second Preview) 17 4 formes autorisées : Statique ou d’instance Avec ou sans paramètre class HelloWorld { void main() { System.out.println("Hello world"); } } La méthode main() Sélection de la méthode main() en 2 étapes : Invocation d’une méthode candidate avec un paramètre String[], si existe Sinon invocation d’une méthode candidate sans paramètres si existe sinon erreur Pas d’ambiguïté car une méthode statique et d’instance ne peuvent pas avoir la même signature Visibilité : public, protected, package-private (par défaut)
  • 18. Sciam Implicitly Declared Classes and Instance Main Methods (Second Preview) 18 void main() { System.out.println("Hello world"); } Requière un constructeur par défaut Créé à la compilation Aucun autre constructeur possible Déclaration implicite d’une classe dans le package par défaut Dont il n’est pas possible de faire référence Car son nom est déterminé à la compilation Peut avoir des membres Pas de référence de méthodes statiques static String WORLD = "world"; void main() { System.out.print("Hello"); Util.afficher(" "+WORLD); } class Util { static void afficher(String message) { System.out.println(message); } } Le nom du fichier source est libre Pas de Javadoc
  • 19. Sciam Statements before super(...) (Preview) Historiquement la première instruction d’un constructeur Est obligatoirement l’invocation d’un constructeur de la classe ou super-classe Explicitement (this() ou super()) ou implicitement par le compilateur 19 Afin de garantir l’initialisation des champs Requérant des solutions Peu élégantes public class MonEntierPositif extends MonEntier { public MonEntierPositif(long valeur) { super(valeur); if (valeur < 0) throw new IllegalArgumentException("La valeur non positive"); } } public MonEntierPositif(long valeur) { super(verifier(valeur)); } private static long verifier(long valeur) { if (valeur < 0) throw new IllegalArgumentException("La valeur non positive"); return valeur; } Parfois contraignant
  • 20. Sciam But : permettre d’avoir dans les constructeurs des traitements Qui ne font pas référence à l'instance avant l'invocation explicite du constructeur 20 Dans ce prologue : Impossible d’utiliser this.xxx ou super.xxx explicite ou implicite Impossible de référencer un champ d’une classe englobante C.this.xxx public class MonEntierPositif extends MonEntier { public MonEntierPositif(long valeur) { if (valeur < 0) throw new IllegalArgumentException("La valeur non positive"); super(valeur); } } Statements before super(...) (Preview)
  • 21. Sciam Les fonctionnalités du projet Loom Structured Concurrency (Second Preview) Scoped Values (Second Preview)
  • 22. Sciam Structured Concurrency (Second Preview) Propose un nouveau modèle de programmation Grâce au traitement de plusieurs tâches Exécutées dans différents threads virtuels comme une seule unité de travail 22 Fork/Join et concurrence structurée sont complémentaires Le but : simplifier la programmation multithread En rationalisant la gestion des erreurs et l'annulation, En améliorant la fiabilité et en renforçant l'observabilité Fork/Join Conçu pour traiter des tâches à forte intensité de calcul sur une courte durée Utilise des threads de l’OS Complexe à mettre œuvre Concurrence structurée Conçue pour traiter des tâches à forte intensité d'E/S Utilise des threads virtuels Facile à mettre en œuvre
  • 23. Sciam API en incubation en Java 20 Classe principale : java.util.concurrent.StructuredTaskScope 23 Le modèle permet une écriture du code dans un style synchrone Avec une exécution en asynchrone Le code est ainsi facile à écrire, à lire et à tester API en preview en Java 21 Dans le package java.util.concurrent Second preview en Java 22 sans modification Structured Concurrency (Second Preview)
  • 24. Sciam Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; return resultat; }  Créer une instance dans un try-with-resource Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  } return resultat; } La mise en œuvre en plusieurs étapes 24 Structured Concurrency (Second Preview)  Invoquer la méthode fork() pour chaque sous-tâche à exécuter Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); } return resultat; }  Attendre la fin de l’exécution des sous-tâches • Soit sans timeout en utilisant la méthode join() • Soit avec timeout en utilisant la méthode joinUntil() Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15));  } return resultat; }  Exploiter les résultats obtenus dans des instances de type Subtask Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15));  resultat = this.genererFacture(clientFuture.get(), commandeFuture.get());  } return resultat; }
  • 25. Sciam La classe StructuredTaskScope.ShutdownOnFailure Propose un modèle invoke all 25 Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Subtask <Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15)); scope.throwIfFailed(); resultat = this.genererFacture(clientFuture.get(), commandeFuture.get()); } return resultat; } Qui exécute toutes les sous-tâches Et termine toutes les sous-tâches en cours si une sous-tâche lève une exception Structured Concurrency (Second Preview)
  • 26. Sciam La classe StructuredTaskScope.ShutdownOnSuccess Propose un modèle invoke any 26 Temperature getTemperature(String ville) throws InterruptedException, ExecutionException { Temperature resultat = null; try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Temperature>()) { serviceMeteos.forEach(f -> { scope.fork(() -> f.getTemperature(ville)); } ); scope.join(); resultat = scope.result(); } return resultat; } Qui renvoie le résultat de la première sous-tâche terminée Et termine les autres sous-tâches restantes Structured Concurrency (Second Preview)
  • 27. Sciam Possibilité de créer son propre scope En héritant de la classe StructuredTaskScope Et en y implémentant ses propres règles métiers 27 class ComposantLePlusLegerScope extends StructuredTaskScope<Composant> { private final Collection<Composant> composants = new ConcurrentLinkedQueue<>(); private final Collection<Throwable> exceptions = new ConcurrentLinkedQueue<>(); @Override protected void handleComplete(Subtask<? extends Composant> subtask) { switch (subtask.state()) { case SUCCESS -> this.composants.add(subtask.get()); case FAILED -> this.exceptions.add(subtask.exception()); case UNAVAILABLE -> {} } } Structured Concurrency (Second Preview)
  • 28. Sciam 28 public Exception exceptions() { RuntimeException exception = new RuntimeException("Impossible d'obtenir le composant le plus leger"); exceptions.forEach(exception::addSuppressed); return exception; } public Composant getComposant() throws Exception { return composants.stream().min(Comparator.comparing(Composant::poids)) .orElseThrow(this::exceptions); } } Structured Concurrency (Second Preview)
  • 29. Sciam Scoped Values (Second Preview) Pour partager des objets dans le code exécuté par un thread Historiquement depuis Java 1.2, on utilise une variable de type ThreadLocal L’API ScopedValue tente de remédier à ces inconvénients 29 Mais cela présente plusieurs risques : Mutable, fuite de mémoire, consommation de ressources public final static ScopedValue<String> VALEUR = ScopedValue.newInstance(); Création d’une instance généralement statique et publique Stocker et de partager des données immuables Pour une durée de vie limitée à des traitements du thread qui les a écrits En incubation dans Java 20 (JEP 429) En preview dans Java 21 (JEP 446) et Java 22 sans modification
  • 30. Sciam Scoped Values (Second Preview) where() pour définir une valeur, chainable pour plusieurs valeurs get() pour obtenir la valeur ou lève une NoSuchElementException 30 Pour exécuter une tâche dans le thread courant System.out.println((VALEUR.isBound() ? VALEUR.get() : "non definie")); isBound() pour savoir si une valeur est associée au thread run() : sous la forme d’une implémentation de Runnable ScopedValue.where(VALEUR, "test").run(() -> { afficherValeur(); }); Ou call() : sous la forme d’une implémentation de Callable String valeur = ScopedValue.where(VALEUR, "test") .<String>call(monService::traiter);
  • 31. Sciam Scoped Values (Second Preview) Réassociation d’une valeur pour un traitement sous-jacent Partage avec les threads virtuels d’une StucturedTaskScope 31 ScopedValue.where(VALEUR, "valeur", () -> { try (var scope = new StructuredTaskScope<String>()) { afficherValeur(); scope.fork(monServiceA::traiter); scope.fork(monServiceB::traiter); scope.joinUntil(Instant.now().plusSeconds(10)); } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); } }); ScopedValue.where(VALEUR, "valeur").run(() -> { afficherValeur(); // valeur ScopedValue.where(VALEUR, "autre-valeur").run(monService::traiter); // autre-valeur afficherValeur(); // valeur });
  • 32. Sciam Les fonctionnalités du projet Panama Foreign Function & Memory API Vector API (Seventh Incubator)
  • 33. Sciam Foreign Function & Memory API API de bas niveau pour de manière simple, sûre et efficace : • Accéder à des données en mémoire hors du tas (off heap memory) • Invoquer des fonctions natives Proposée en preview en Java 19 (JEP 424), 20 (JEP 434) et 21 (JEP 442) Elle est dans le package java.lang.foreign du module java.base 33 Historiquement, fusion de 2 JEPs introduites en incubation : Foreign-Memory Access API en Java 14 (JEP 370, 383, et 393) Et Foreign Linker API en Java 16 (JEP 389) Proposée en incubation en Java 17 (JEP 412) et Java 18 (JEP 419) En Standard en Java 22
  • 34. Sciam L’API de bas niveau 2) Pour invoquer du code natif Une alternative à l’API JNI présente depuis Java 1.1 34 1) Pour accéder à des données En mémoire hors du tas (off heap memory) De manière sûre et performante Alternative à certaines fonctionnalités De java.nio.ByteBuffer (pas performante mais sûre) Et sun.misc.Unsafe (non standard) Foreign Function & Memory API Requière une bonne connaissance des mécanismes natifs De bas niveau utilisés (adresses, segments, formats des données,…)
  • 35. Sciam API dans le package java.lang.foreign du module java.base En utilisant des fonctionnalités d’Invoke Dynamique VarHandle pour l’accès accès aux données MethodHandle pour l’invocation de méthodes 35 Définit des classes et des interfaces permettant : De contrôler l'allocation et la désallocation de la mémoire étrangère : MemorySegment, SegmentAllocator, Arena Foreign Function & Memory API D’appeler des fonctions étrangères : SymbolLookup, FunctionDescriptor, Linker De manipuler et accéder à la mémoire étrangère structurée : MemoryLayout et dérivées Autoriser les appels natifs --enable-native-access=ALL-UNNAMED Pour éviter un warning et dans le futur une erreur pour des raisons de sécurité
  • 36. Sciam Foreign Function & Memory API Contrôle de l'allocation et la désallocation de la mémoire étrangère 36 L’interface Arena qui hérite de SegmentAllocator Gère l’accès à la mémoire native et leur isolation dans une portée Et s’assure que les segments alloués seront libérés et qu’il n’y a pas d’accès après libération global arena : une seule, partagée par tous les threads, Libération des segments à l’arrêt de la JVM Arena arena = Arena.global(); automatic arena : gérée par le ramasse-miettes, Utilisable par plusieurs threads, Libération des segments lorsqu’il n’y a plus de référence Arena arena = Arena.ofAuto(); confined arena : les segments sont libérés à la fermeture De l’arena, utilisable que par le thread qui l’a créé try (Arena arena = Arena.ofConfined()) { . . . } shared arena : combine un confined arena Avec une utilisation multi-threads Arena arena = Arena.ofShared(); 4 Arena proposées :
  • 37. Sciam Foreign Function & Memory API MemorySegment décrit une zone de mémoire off heap Arena::allocateFrom(String) Helper qui alloue un segment pour une chaîne en UTF-8 au format C MemorySegment chaineNative = arena.allocateFrom("Java"); Arena::allocate(MemoryLayout) Pour allouer un segment correspondant à une structure Arena::allocate(long) Alloue un segment dont la taille en octets est précisée Allocation avec surcharges de méthodes de SegmentAllocator MemorySegment segment = arena.allocate(100); Notamment : 37
  • 38. Sciam Foreign Function & Memory API Manipulation et accès à la mémoire étrangère structurée 38 MemoryLayout définit la structure de mémoire d'un type spécifique ValueLayout hérite de MemoryLayout Définit la façon dont les types de base sont stockés en mémoire try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(10 * 4); // allouer pour 10 entiers de type int (4 octets) for (int i = 0 ; i < 10 ; i++) { segment.setAtIndex(ValueLayout.JAVA_INT, i, i); } } Exemple : ValueLayout.JAVA_LONG décrit le type Java primitif long ValueLayout.ADDRESS décrit une adresse mémoire du matériel sous-jacent
  • 39. Sciam Foreign Function & Memory API SequenceLayout hérite de MemoryLayout Décrit un tableau d'un type spécifique décrit par un MemoryLayout 39 StructLayout hérite de MemoryLayout Décrit une structure de types qui se suivent en mémoire StructLayout dateStruct = MemoryLayout.structLayout( ValueLayout.JAVA_SHORT.withName("annee"), ValueLayout.JAVA_SHORT.withName("mois"), ValueLayout.JAVA_SHORT.withName("jour")); // tableau de 10 doubles Java MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_DOUBLE); // un tableau composé de trois tableaux de dix tableaux entiers MemoryLayout.sequenceLayout(3, MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT));
  • 40. Sciam Foreign Function & Memory API Manipulation de bas niveau d’un segment 40 SequenceLayout pointsLayout = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y"))); VarHandle xHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x")); VarHandle yHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y")); MemorySegment segment = arena.allocate(pointsLayout); for (int i = 0; i < pointsLayout.elementCount(); i++) { xHandle.set(segment, (long) i, i); yHandle.set(segment, (long) i, i); } MemorySegment segment = arena.allocate((long)(2 * 4 * 10), 1); for (int i = 0; i < 10; i++) { segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2), i); // x segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2) + 1, i); // y } Manipulation d’un segment structuré avec des VarHandles
  • 41. Sciam Foreign Function & Memory API Recherche et description de fonctions étrangères 41 L’interface SymbolLookup Pour localiser et rechercher l’adresse de fonctions dans les bibliothèques natives L’interface FunctionDescriptor Décrit les paramètres entrants et sortants d'une fonction native Chargement optionnel de la bibliothèque native par le ClassLoader Avec System::loadLibrary Des fabriques pour obtenir une instance : static SymbolLookup loaderLookup() pour une instance sur les bibliothèques chargées par le ClassLoader static SymbolLookup libraryLookup(String, Arena) et libraryLookup(Path, Arena) pour une instance sur une bibliothèque qui sera chargée au besoin Lors de l'appel de fonction native avec un MethodHandle s'assure que les types Java transférés sont convertis en types C corrects et que la valeur de retour est convertie d'un type C vers le type de retour Java
  • 42. Sciam Foreign Function & Memory API Obtenir un éditeur de liens spécifique à l’Application Binary Interface 42 Linker Crée un éditeur de liens spécifique à l’ABI de la plate-forme native sous-jacente Linker linker = Linker.nativeLinker(); Une ABI est un ensemble de conventions d’appel et de types de données Associés au compilateur, au système d’exploitation et au processeur utilisés Optimisé pour les conventions d'appel de nombreuses plateformes : Linux/x64 Linux/AArch64 Linux/RISC-V Linux/PPC64 Linux/s390 macOS/x64 macOS/AArch64 Windows/x64 Windows/AArch64 AIX/ppc64 Prend en charge les conventions d'appel des autres plateformes en déléguant à libffi SymbolLookup stdlib = linker.defaultLookup(); La méthode defaultLookup() renvoie un SymbolLookup Pour rechercher une fonction dans un ensemble de bibliothèques courantes Exemple : libc sous Linux
  • 43. Sciam Foreign Function & Memory API Appel à des fonctions étrangères via des MethodHandles 43 Linker permet une interaction aller-retour Entre le code Java et les fonctions étrangères MethodHandle strlen = linker.downcallHandle( linker.defaultLookup().find("strlen").get(), FunctionDescriptor.of(JAVA_LONG, ADDRESS) ); try (Arena arena = Arena.ofConfined()) { MemorySegment chaine = arena.allocateFrom("Java"); long len = (long) strlen.invoke(chaine); } Les appels ascendants (upcall) Appels du code natif vers le code Java Les appels descendants (downcall) Appels du code Java vers le code natif
  • 44. Sciam public class DialogFFM { public static void main(String[] args) { try { System.loadLibrary("user32"); Optional<MemorySegment> msgBoxFunction = SymbolLookup.loaderLookup().find("MessageBoxA"); FunctionDescriptor msgBoxFunctionDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, ADDRESS, JAVA_INT); Linker linker = Linker.nativeLinker(); MethodHandle methodHandle = linker.downcallHandle(msgBoxFunction.get(), msgBoxFunctionDesc); try (Arena offHeap = Arena.ofConfined()) { MemorySegment cStringMessage = offHeap.allocateFrom("Voulez-vous utiliser Java 22 ?"); MemorySegment cStringTitre = offHeap.allocateFrom("Confirmation"); int bouton = (int) methodHandle.invoke(NULL, cStringMessage, cStringTitre, 36); } } catch (Throwable t) { t.printStackTrace(); } } } 44 Foreign Function & Memory API Exemple sous Windows
  • 45. Sciam Vector API (Seventh Incubator) Exprimer des calculs vectoriels Qui, au moment de l'exécution, sont systématiquement compilés Avec les meilleures instructions vectorielles possibles sur l’architectures CPU Les SIMD sur les CPU supportées : x64 (SSE et AVX) et AArch64 (Neon) Dans le module jdk.incubator.vector 45 Single Instruction, Multiple Data Traiter en parallèle un tableau de données, sans threads Pour appliquer une même opération sur plusieurs valeurs traitées En un seul cycle de traitement CPU En incubation en Java 16 (JEP 338), 17 (JEP 414), 18 (JEP 417), 19 (JEP 426), 20 (JEP 438) et 21 (JEP 448)
  • 46. Sciam static float[] calculerScalaire(float[] a, float[] b) { float[] c = new float[a.length]; for (int i = 0 ; i < a.length ; i++) { c[i] = a[i] * a[i] - b[i] * b[i]; } return c; } 46 API plutôt de bas niveau, verbeuse, dépendant partiellement du CPU Mais qui peut offrir de meilleures performances que le code scalaire équivalent Exemple Java 22 static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED; static float[] calculerVectoriel(float[] a, float[] b) { float[] c = new float[a.length]; int i = 0; for (; i < SPECIES.loopBound(a.length) ; i += SPECIES.length()) { var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var vr = va.mul(va).sub(vb.mul(vb)); vr.intoArray(c, i); } for (; i < a.length; i++) { c[i] = a[i] * a[i] - b[i] * b[i]; } return c; } Vector API (Seventh Incubator)
  • 47. Sciam Les évolutions dans les API de Java Core Stream Gatherers (Preview) Class-File API (Preview)
  • 48. Sciam Stream Gatherers (Preview) L'API Stream fournit un ensemble complet mais fixe d'opérations Intermédiaires et terminales : filtrage, transformation, réduction, tri, … 48 Le but : proposer l’opération intermédiaire Stream::gather extensible Qui permet d’exprimer quasiment toutes les implémentations voulues En utilisant l’API Gatherer similaire à l’API Collector Une opération terminale extensible Stream::collect Qui permet de produire le résultat d'un pipeline de diverses manières En utilisant l’API Collector Mais ce nombre fixe d’opérations empêche d’exprimer Certains traitements complexes Plusieurs opérations intermédiaires ont déjà été ajoutées Mais ce n’est pas solution maintenable dans le temps
  • 49. Sciam Stream Gatherers (Preview) Attend en paramètre une implémentation de l’interface Gatherer 49 Elle définit quatre fonctions : default BiConsumer<A,Gatherer.Downstream<? super R>> finisher() Optionnelle, invoquée lorsqu'il n'y a plus d'éléments à traiter Peut utiliser l'objet d'état privé Pour éventuellement, émettre des éléments de sortie supplémentaires Gatherer.Integrator<A,T,R> integrator() Intègre un nouvel élément du flux d'entrée éventuellement avec l'objet d'état privé Pour émettre éventuellement des éléments vers le flux de sortie Peut interrompre le traitement avant d'atteindre la fin du flux d'entrée default Supplier<A>initializer() Fonction d'initialisation facultative fournit un objet Qui conserve un état privé pendant le traitement des éléments du flux default BinaryOperator<A> combiner() Optionnelle, utilisée pour combiner les gatherers Lorsque le flux d'entrée est marqué comme parallèle
  • 50. Sciam Stream Gatherers (Preview) L’interface fonctionnelle Gatherer.Downstream<T> 50 La méthode abstraite boolean push(T element) Pour envoyer éventuellement l’élément en sortie Elle renvoie un booléen : false pour short cirtuiting sinon true L’interface fonctionnelle Gatherer.Integrator<A, T, R> Une instance est retournée par l’integrator Une instance est fournie à l’integrator et au finisher La méthode boolean integrate(A state, T element, Downstream<? super R> downstream) Pour traiter un élément entrant et renvoyer un booléen (false pour short cirtuiting) Function<Personne, String> mapper = // . . . Transforme une Personne en String Integrator<Void, Personne, String> integrator = (state, element, downstream) -> { R mappedElement = mapper.apply(element); downstream.push(mappedElement); return true; };
  • 51. Sciam Stream Gatherers (Preview) L’interface Gatherer propose aussi différentes surcharges de la fabrique of() Pour obtenir un Gatherer À partir de l’implémentation d’une ou plusieurs des quatre fonctions 51 Exemple : un Gatherer équivalent à l’opération map() public static <T, R> Gatherer<T, Void, R> mapping(Function<? super T, ? super R> mapper) { return Gatherer.of( (_ , element, downstream) -> { R mapped = mapper.apply(element); downstream.push(mapped); return true; }); } Un Gatherer peut être : Exécuté en séquentiel ou parallèle Stateless ou statefull Short-Circuiting ou greedy
  • 52. Sciam Stream Gatherers (Preview) La classe Gatherers propose des fabriques pour usages courants 52 fold(Supplier<R> initial, BiFunction<? super R,? super T,? extends R> folder) Renvoie un Gatherer qui construit un agrégat de manière incrémentielle Et émet cet agrégat lorsqu'il n'y a plus d'éléments d'entrée scan(Supplier<R> initial, BiFunction<? super R,? super T,? extends R> scanner) Renvoie un Gatherer 1-1 qui applique une fonction fournie À l'état actuel et à l'élément pour produire l'élément suivant, qu'il transmet en sortie List<String> nombreStr = Stream.of(1, 2, 3, 4, 5) .gather(Gatherers.fold(() -> "", (string, number) -> !string.isEmpty() ? string + ";" + number : string + number)) .toList(); System.out.println(nombreStr); // [1;2;3;4;5] List<String> nombreStrs = Stream.of(1, 2, 3, 4, 5) .gather(Gatherers.scan(() -> "", (string, number) -> string + number)) .toList(); System.out.println(nombreStrs); // [1, 12, 123, 1234, 12345]
  • 53. Sciam Stream Gatherers (Preview) windowFixed(int windowSize) Renvoie un Gatherer n-m qui regroupe les éléments d'entrée Dans des listes d'une taille donnée Et transmet les listes en sortie lorsqu'elles sont pleines 53 windowSliding(int windowSize) Renvoie un Gatherer n-m qui regroupe les éléments d'entrée Dans des listes d'une taille fournie Après la première fenêtre, chaque liste suivante est créée à partir d'une copie de La précédente en supprimant le premier élément Et en ajoutant l'élément suivant à partir du flux d'entrée List<List<Integer>> windowsFixed = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).gather(Gatherers.windowFixed(3)).toList(); System.out.println(windowsFixed); // [[1, 2, 3], [4, 5, 6], [7, 8]] List<List<Integer>> windowSlicing = Stream.of(1, 2, 3, 4, 5).gather(Gatherers.windowSliding(3)).toList(); System.out.println(windowSlicing); // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
  • 54. Sciam Stream Gatherers (Preview) mapConcurrent(int maxConc, Function<? super T,? extends R> mapper) Renvoie un Gatherer 1-1 qui invoque une fonction fournie Sur chaque élément d'entrée en parallèle, jusqu'à une limite fournie 54
  • 55. Sciam Stream Gatherers (Preview) Les gatherers supportent la composition 55 via la méthode andThen(Gatherer) Qui joint deux gatherers Où le premier produit des éléments que le second peut consommer Cela permet de créer des gatherers sophistiqués En composant des gatherers plus simples Tout comme la composition de fonctions Ainsi sémantiquement : source.gather(a).gather(b).gather(c).collect(...) Est équivalent à : source.gather(a.andThen(b).andThen(c)).collect(...)
  • 56. Sciam Class-File API (Preview) L'écosystème Java dispose de plusieurs bibliothèques Pour manipuler le bytecode Toutes hors du JDK : ASM, BCEL, Javassist, ByteBuddy, … 56 But : fournir une API standard Pour l'analyse, la génération et la transformation de fichier de classe Java Le JDK utilise en interne ASM Dans une version N-1 par rapport à la version N du JDK Dans le package java.lang.classfile Utilisées par de nombreux frameworks
  • 57. Sciam Class-File API (Preview) API riche pour permettre un accès random ou séquentiel 57 La génération utilise des builders fournis en paramètre d’interfaces fonctionnelles Les différents composants sont modélisés : xxxModel représentent des structures complexes, immuables telles que des classes, des méthodes, des champs, le corps d'une méthode xxxElement représentent un élément du fichier de classe, immuables Les Elements peuvent être des Models et un Model possède un Element correspondant xxxEntry représentent les éléments du constant pool (PoolEntry, ClassEntry, Utf8Entry) Également exposés sous la forme de Model et d’Element Attribute représentent un attribut d’un élément Majoritairement exposés sous la forme d’Element API moderne Utilisation de fabriques, de types scellés, d’immutabilité, … Utilise les types du package java.lang.constant pour les informations symboliques
  • 58. Sciam Class-File API (Preview) La génération de fichiers de classe 58 import java.lang.classfile.ClassFile; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.nio.file.Path; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.CD_int; import static java.lang.constant.ConstantDescs.CD_long; public class TestClassFile { public static void main(String[] args) throws java.io.IOException { ClassFile.of().buildTo(Path.of("EntierUtils.class"), ClassDesc.of("EntierUtils"), classBuilder -> classBuilder.withMethodBody("ajouter", MethodTypeDesc.of(CD_long, CD_int, CD_int), ACC_PUBLIC | ACC_STATIC, codeBuilder -> codeBuilder.iload(1) .i2l() .iload(2) .i2l() .ladd() .lreturn())); } } C:javaTestJava22> javap -c .EntierUtils.class public class EntierUtils { public static long ajouter(int, int); Code: 0: iload_1 1: i2l 2: iload_2 3: i2l 4: ladd 5: lreturn } Equivalent à (sans constructeur par défaut) public class EntierUtils { public static long ajouter(int a, int b) { return (long) a + b; } }
  • 59. Sciam Class-File API (Preview) La lecture et l’analyse des fichiers de classe Avec plusieurs formes de parcours proposées 59 ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class")); for (ClassElement ce : cm) { switch (ce) { case MethodModel mm -> { System.out.printf("Methode %s%s%n", mm.methodName().stringValue(), mm.methodTypeSymbol().displayDescriptor()); for (MethodElement me : mm) if (me instanceof CodeModel xm) for (CodeElement el : xm) System.out.printf(" %s%n", el); } default -> {} } } Methode ajouter(int,int)long Load[OP=ILOAD_1, slot=1] UnboundConvertInstruction[op=I2L] Load[OP=ILOAD_2, slot=2] UnboundConvertInstruction[op=I2L] UnboundOperatorInstruction[op=LADD] Return[OP=LRETURN] Débute par l’obtention d’un ClassModel En invoquant une surcharge de ClassFile::parse Parcours des éléments encapsulés dans un type ClassElement Car ClassModel implémente Iterable<ClassElement>
  • 60. Sciam Class-File API (Preview) Parcours des éléments avec un Stream 60 ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class")); for (MethodModel mm : cm.methods()) { System.out.printf("Methode %s%s%n", mm.methodName().stringValue(), mm.methodTypeSymbol().displayDescriptor()); for (MethodElement me : mm) if (me instanceof CodeModel xm) for (CodeElement el : xm) System.out.printf(" %s%n", el); } Parcours des éléments selon leur type En invoquant des méthodes dédiées : fields(), methods() ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class")); cm.elementStream() .flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty()) .flatMap(me -> me instanceof CodeModel com ? com.elementStream() : Stream.empty()) .forEach(com -> System.out.printf(" %s%n", com));
  • 61. Sciam Class-File API (Preview) La transformation de fichiers de classe 61 Souvent la combinaison de lecture et écriture Où un fichier de classe est lu, des modifications localisées sont apportées, Mais une grande partie du fichier de classe est transmise sans modification Chaque XxxBuilder dispose d'une méthode with(XxxElement) Qui permet d’inclure directement les éléments sans les modifier ClassModel cm = ClassFile.of().parse(Path.of("EntierUtils.class")); byte[] newBytes = ClassFile.of().build(cm.thisClass().asSymbol(), classBuilder -> { for (ClassElement ce : cm) { if (!(ce instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter"))) { classBuilder.with(ce); } } }); Exemple : retirer la méthode ajouter()
  • 62. Sciam Class-File API (Preview) Une transformation peut être considérée comme une « flatMap » Sur la séquence d'éléments 62 ClassTransform ct = (builder, element) -> { if (!(element instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter"))) builder.with(element); }; var cc = ClassFile.of(); byte[] newBytes = cc.transform(cc.parse(Path.of("EntierUtils.class")), ct); Chaque Model a un type Transform correspondant ClassTransform::dropping helper pour retirer des éléments ClassTransform ct = ClassTransform.dropping( element -> element instanceof MethodModel mm && mm.methodName().stringValue().equals("ajouter"));
  • 63. Sciam Class-File API (Preview) Combinaison de transformations 63 Exemple : remplacer tous les opcodes ladd par lsub Pour faire manuellement une transformation pour du mutation testing CodeTransform laddToLsub = (b, e) -> { if (e instanceof Instruction i && i.opcode() == Opcode.LADD) b.lsub(); else b.with(e); }; Définition d’un CodeTransform MethodTransform mt = MethodTransform.transformingCode(laddToLsub); ClassTransform ct = ClassTransform.transformingMethods(mt); Définition d’un MethodTransform utilisant le CodeTransform et d’un ClassTransform ClassTransform ct = ClassTransform.transformingMethodBodies(laddToLsub); Ou plus simplement var cc = ClassFile.of(); byte[] newBytes = ClassFile.of().transform(cc.parse(Path.of("EntierUtils.class")), ct); Application de la transformation
  • 64. Sciam La classe ListFormat Pour formater ou analyser une liste de chaîne de caractères D’une manière sensible aux paramètres régionaux (Locale) 64 Le type de mise en forme avec l’énumération ListFormat.Type Qui détermine la ponctuation entre les chaînes et les mots de liaison, le cas échéant Valeurs définies : STANDARD (par défaut), OR et UNIT var elements = List.of("E1", "E2", "E3"); System.out.println(ListFormat.getInstance().format(elements)); System.out.println(ListFormat.getInstance(Locale.FRANCE, OR, FULL).format(elements)); System.out.println(ListFormat.getInstance(Locale.US, STANDARD, FULL).format(elements)); System.out.println(ListFormat.getInstance(Locale.US, STANDARD, SHORT).format(elements)); E1, E2 et E3 E1, E2 ou E3 E1, E2, and E3 E1, E2, & E3 Une instance est obtenue avec une fabrique à qui on peut passer : Une locale, sinon la locale par défaut sera utilisée Le style de mise en forme avec l’énumération ListFormat.Style Qui adapte la façon dont les chaînes sont abrégées (ou non) selon le type Valeurs définies : FULL (par défaut), SHORT et NARROW
  • 65. Sciam Ajout divers dans l’API Java Core boolean Console.isTerminal() Retourne un booléen qui indique si l’instance de la console est un terminal 65 static class<?> Class.forPrimitiveName(String) Retourne la classe associée au type primitif donné sinon null Les 9 types primitifs sont boolean, byte, char, short, int, long, float, double et void static InetAddress InetAddress.ofLiteral(String) Créé une instance depuis la représentation textuelle de l’adresse IP Existe aussi pour les classes Inet4Address et Inet6Address Default DoubleStream RandomGenerator.equiDoubles(double left, double right, boolean isLeftIncluded, boolean isRightIncluded) Renvoie un Stream illimité de valeurs doubles choisies de manière pseudo-aléatoire, Où chaque valeur est comprise entre la limite gauche et la limite droite spécifiées Incluses ou non avec une garantit une distribution uniforme
  • 66. Sciam Les évolutions dans la JVM HotSpot Launch Multi-File Source-Code Programs Region Pinning for G1
  • 67. Sciam Launch Multi-File Source-Code Programs Depuis le JDK 11, la JVM peut exécuter UN unique fichier .java Sans avoir à le compiler en préalable ( JEP 330 ) Pouvant contenir plusieurs types 67 Exécuter la JVM avec le fichier .java Une compilation en mémoire est effectuée avant l’exécution public class Hello { public static void main(String[] args) { Utils.saluer(); } interface Utils { static void saluer() { System.out.println("Hello"); } } } C:java>java Hello.java Hello C:java>dir 14/02/2024 17:40 <DIR> . 16/01/2024 10:14 <DIR> .. 14/02/2024 17:40 195 Hello.java
  • 68. Sciam Launch Multi-File Source-Code Programs But : permettre l’exécution de code dans plusieurs fichiers source 68 Impossible d'avoir un même type déclaré dans plusieurs fichiers source Pas de garantie d’ordre ou de temporalité pour la compilation .java Compilation à la volée ou lazy Si une classe utilisée est dans le même fichier .java Elle n’est pas recherchée et est compilée et utilisée Seuls les .java dont les types sont utilisés par l’application sont compilés public class Hello { public static void main(String[] args) { Utils.saluer(); } } public interface Utils { static void saluer() { System.out.println("Hello"); } } C:java>java Hello.java Hello C:java>dir /W [.] [..] Hello.java Utils.java
  • 69. Sciam Region Pinning for G1 JNI a besoin de définir et libérer des pointeurs sur des objets Java Les traitements sont lors exécutés dans une section dite critique Ces objets ne doivent pas être déplacés par le GC 69 But : Réduire la latence en permettant l'épinglage des régions par G1 Contenant un objet d’une section critique de JNI Historiquement, G1 suspend tous ses traitements Lors de l’exécution de régions critiques de JNI Pouvant impliquer un risque de bloquer les threads de l’application Voir même une OutOfMemoryError Les régions sans section critique peuvent être traitées Évitant de bloquer les autres threads de l’application pouvant nécessiter le GC Améliore les performances et réduit les risques avec JNI
  • 70. Sciam Les événements JFR Plusieurs événements JFR ajoutés dans la JVM HotSpot 70 CompilerQueueUtilization ( JDK-8317562 ) Statistiques des queues des compilateur du JIT Un événement pour C1 et C2 DeprecatedInvocation ( JDK-8211238 ) Invocation unique d’une méthode annotée avec @Deprecated du JDK NativeLibraryLoad ( JDK-8313251 ) et NativeLibraryUnLoad ( JDK-8314211) Informations sur une opération de chargement / déchargement D’une bibliothèque dynamique ou une image native
  • 71. Sciam Les autres évolutions dans les GC Plusieurs améliorations ont été apportées dans les GC 71 G1 GC Code root scan causes long GC pauses due to imbalanced iteration (JDK-8315503) Fast Collection of Evacuation Failed Regions (JDK-8140326) More Deterministic Heap Resize at Remark (JDK-8314573) Parallel GC amélioration du throughput avec des grands tableaux (JDK-8321013) Precise Parallel Scanning of Large Object Arrays for Young Collection Roots (JDK-8310031) Serial GC Better GC Throughput with Scarce Dirty Cards (JDK-8319373)
  • 72. Sciam Les évolutions dans les outils du JDK Les évolutions dans javac
  • 73. Sciam Avant le JDK 22, le compilateur javac acceptait les références de méthode privées avec comme récepteur une variable de type 73 import java.util.function.*; class Fonction { private String asString() { return "test"; } static <T extends Fonction> Function<T, String> get() { return T::asString; } } Avec le JDK 22, le compilateur javac émet une erreur C:java>javac Fonction.java Fonction.java:9: error: asString() has private access in Fonction return T::asString; ^ 1 error Les évolutions dans javac
  • 74. Sciam Avant le JDK 22, le compilateur javac permettait l’utilisation du mot clé final au début d’un record pattern dans le case d’un switch utilisant du pattern matching 74 Object o = new Pays("France"); switch ( o ) { case final Pays(var n) -> {} default -> {} } Avec le JDK 22, le compilateur javac émet une erreur C:java>javac Pays.java Pays.java:6: error: modifier final not allowed here case final Pays(var n) -> {} ^ 1 error Les évolutions dans javac
  • 76. Sciam Conclusion Java poursuit son évolution en respectant son modèle de releases La syntaxe (Amber) 76 Java 22 proposent des fonctionnalités concernant : Cela permettra à Java de rester pertinent aujourd’hui et demain La programmation parallèle et concurrente (Loom) Une meilleure utilisation du matériel moderne (Panama) avec FFM en standard N’hésitez pas à télécharger un JDK 22 auprès d’un fournisseur L’API Stream Gatherers et ClassFile en preview
  • 78. Sciam