saverioriotto.it

Java 21: cosa c'è di nuovo?

Da oggi disponibile Java 21! Sono passati sei mesi dal rilascio di Java 20, ed ecco un'altra nuova ondata di funzionalità. Vediamo le differenze con Java 20 con alcuni casi d'uso tipici, in modo da essere pronti a utilizzare queste nuove funzionalità.

Java 21: cosa c'è di nuovo?

Java 21 contiene cinque funzionalità scaturite dal Progetto Amber:

 - Pattern Matching for switch;
 - Record Patterns;
 - Unnamed Patterns and Variables;
 - Unnamed Classes and Instance Main Methods;
 - String Templates.

L'obiettivo del Progetto Amber è quello di esplorare e incubare funzionalità del linguaggio Java più piccole e orientate alla produttività.

Cosa c’è di nuovo in Java 21? Scopriamolo !

 

Estensioni e aggiornamenti del linguaggio principale che dovresti assolutamente conoscere

Java 21 sposta definitivamente i modelli di record e la corrispondenza dei modelli per i costrutti switch dalle preview alle funzionalità pronte all'uso. Ciò significa che ora puoi utilizzarle nei tuoi progetti poiché sono definitivi. Entrambi migliorano la leggibilità del tuo codice. I modelli e le variabili senza nome (Unnamed patterns and variables) intendono migliorare ulteriormente la leggibilità dei modelli di record, ma sono ancora in una fase di preview, il che significa che ancora sarebbe opportuno aspettare prima di utilizzarle.

Hai mai avuto la necessità di aver bisogno di un ordine chiaramente definito degli elementi nelle tue collezioni? Java 21 per offrirti questa capacità introduce raccolte sequenziate.

I modelli di stringa (String templates) sono la versione di Java dell'interpolazione delle stringhe, ovvero il processo di valutazione di una stringa letterale contenente un placeholder. Ciò offre leggibilità e vantaggi in termini di sicurezza, quindi assicurati di aggiornare il tuo know-how Java con modelli di stringhe!

Infine, le classi senza nome (unnamed classes) hanno lo scopo di rendere più semplice la vita dei principianti, ma sono anche una caratteristica interessante quando si sta prototipando un semplice helper da riga di comando e si desidera eliminare la solita confusione di "public static void main(String[] args )”.

440: Record Patterns

I modelli di record mirano a migliorare la navigazione e l'elaborazione dei dati. Abilitano modelli nidificati, consentendo di utilizzare query di dati più sofisticate. I record patterns erano una funzionalità di anteprima sia in JDK 19 che in JDK 20 e la nuova versione JDK 21 finalizza la funzionalità in base all'esperienza e al feedback raccolti dalle versioni precedenti.

I modelli di record si sono evoluti insieme alla corrispondenza dei modelli per le espressioni e le istruzioni switch, che la nuova funzionalità estende per passare alle istanze destrutturate delle classi di record.

Per comprendere meglio i modelli di record, consulta il seguente esempio di codice tratto da JEP 440: 

record Point(int x, int y) {}

static void printSumWithoutPatternMatching(Object obj) {
 if (obj instanceof Point p) {
   int x = p.x();
   int y = p.y();
   System.out.println(x+y);
 }
}

static void printSumWithPatternMatching(Object obj) {
 if (obj instanceof Point(int x, int y)) {
  System.out.println(x+y);
 }
}

Il metodo printSumWithoutPatternMatching mostra come apparivano i campi x e y senza modelli di record. Il secondo metodo printSumWithPatternMatching mostra l'eleganza dei nuovi record di corrispondenza dei modelli. Si noti che le variabili x e y vengono dichiarate automaticamente utilizzandole all'interno del pattern Point(int x, int y).

I modelli di record possono anche essere nidificati, consentendo di destrutturare istanze di classi di record contenenti i record stessi. Il codice seguente mostra la decostruzione di un Rectangle. Tieni presente che il primo ColouredPoint viene ulteriormente decostruito rispetto al secondo nel modello di record fornito: 

record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}


static void printColorOfUpperLeftPoint(Rectangle r) {
 if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
  ColoredPoint lr)) {
  System.out.println(c);
 }
}

441: Pattern Matching for switch

La corrispondenza dei modelli per il costrutto switch è apparsa per la prima volta come anteprima in JDK 17, con la seconda e la terza anteprime fornite nelle versioni successive. JDK 21 finalizza la funzionalità per consentire la continua evoluzione della corrispondenza dei modelli e dei modelli di record.

Questa funzionalità consente di testare un'espressione rispetto a una serie di modelli, ciascuno con un'azione specifica. Ciò semplifica l'espressione di query complesse orientate ai dati in modo più conciso e sicuro. Tutte le espressioni e le istruzioni switch esistenti verranno comunque compilate e la semantica non è cambiata.

L'esempio seguente mostra la maggior parte delle nuove funzionalità dello switch:

static void testStringNew(String response) {
switch (response) {
  case null -> { System.out.println("So quiet Today?"); }
  case String s
  when s.equals("YES") -> {
   System.out.println("You got it");
  }
  case String s
   when s.equalsIgnoreCase("NO") -> {
   System.out.println("Shame");
 }
  case String s -> {
  System.out.println("Sorry?");
  }
 }
}

Nelle istruzioni switch tradizionali, il passaggio a null portava immediatamente a una NullPointerException. Con la nuova possibilità di commutare tra tipi diversi, ha senso consentire anche un caso nullo dedicato.

Per rimanere retrocompatibile con il codice esistente, la semantica di switch è ora la seguente:

- Nel caso in cui l'espressione del selettore valga null e non vi sia un caso null dedicato, uno switch lancia comunque una NullPointerException, preservando così la vecchia semantica delle istruzioni switch.
- Nel caso in cui l'espressione del selettore restituisca null ed è presente un caso null dedicato, il corpo di quel caso viene eseguito senza generare un'eccezione NullPointerException.

L'esempio di codice precedente non produce una NullPointerException nel caso in cui la risposta sia uguale a null ma stampa semplicemente “So quiet Today?”.

Inoltre, ora è possibile cambiare tipo, ad esempio case String s corrisponde se l'espressione del selettore è del tipo String. Per rendere questa nuova capacità ancora più utile, è stata introdotta la parola chiave when per filtrare ulteriormente quali stringhe dovrebbero corrispondere. Nell'esempio precedente, il secondo caso corrisponde solo nel caso in cui la stringa sia uguale a "YES".

Con la possibilità di cambiare tipo, potrebbero corrispondere diversi rami del case. Per semplificare le cose, gli sviluppatori Java hanno deciso di utilizzare semplicemente il primo caso corrispondente e non necessariamente quello più adatto.

443: Unnamed Patterns and Variables (Preview)

I modelli e le variabili senza nome (una funzionalità di anteprima) mirano a migliorare la leggibilità dei modelli di record evitando l'uso non necessario di modelli nidificati. Identificando le variabili che devono essere dichiarate ma che non verranno utilizzate, semplificano anche la manutenzione del codice.

I modelli senza nome corrispondono a un componente record senza indicare il nome o il tipo del componente, mentre le variabili senza nome sono variabili che possono essere inizializzate ma non utilizzate. Il carattere "_" viene utilizzato per denotare sia modelli senza nome che variabili senza nome

Il nostro esempio di codice dalla sezione Record Patterns può quindi essere semplificato in:

static void printColorOfUpperLeftPoint(Rectangle r) {
 if (r instanceof Rectangle(ColoredPoint(_, Color c),_) {
   System.out.println(c);
 }
}

431: Sequenced Collections

Finora, il framework delle collections Java non disponeva di tipi di raccolta comuni che consentissero agli sviluppatori di determinare l’ordine di presentazione. Ciò ha reso più difficile l'esecuzione delle operazioni relative all'ordine delle presentazioni (ad esempio, elaborare gli elementi in ordine inverso è complicato o addirittura impossibile). Le collections in sequenza risolvono questo problema fornendo nuove interfacce per rappresentare raccolte con un ordine di presentazione definito.

L'utilizzo di una collection sequenziata consente agli sviluppatori di definire non solo il primo e l'ultimo elemento, ma anche i successori e i predecessori per tutti gli elementi tra il primo e l'ultimo. Le raccolte in sequenza supportano anche l'elaborazione degli elementi in avanti e all'indietro.

Diamo un'occhiata ai metodi forniti da SequencedCollection:

interface SequencedCollection extends Collection {
 // new method
 SequencedCollection reversed();
 // methods promoted from Deque
 void addFirst(E);
 void addLast(E);
 E getFirst();
 E getLast();
 E removeFirst();
 E removeLast();
}

Il nuovo metodo reversed è particolarmente interessante perché fornisce una nuova visione della raccolta sequenziata che consente di scorrere la raccolta all'indietro utilizzando i tipici costrutti: cicli for, cicli iterator() espliciti, forEach(), stream(), parallelStream() e toArray().

430: String Templates (Preview)

I modelli di stringa sono una funzionalità di preview in JDK 21 e completano i valori letterali di stringa e i blocchi di testo già esistenti in Java. Per produrre risultati specializzati, i modelli di stringa combinano testo letterale (stringhe letterali o blocchi di testo) con espressioni incorporate gestite dai processori di modello (Template Processor).

I vantaggi di questo aggiornamento sono:

- Leggibilità migliorata delle espressioni che mescolano testo ed espressioni
- Maggiore sicurezza dei programmi Java che trasferiscono stringhe da valori forniti dall'utente a un altro sistema (come le query per i database)
- Utilizzo semplificato delle API che accettano stringhe codificate in linguaggi non Java.

L'esempio di codice seguente mostra un semplice modello di stringa:

String name = "Joan";
String info = STR."My name is \{name}";
assert info.equals("My name is Joan"); // true

Nel nostro esempio, STR è un processore di modelli che esegue interpolazioni di stringhe. Viene automaticamente importato staticamente in qualsiasi classe Java e può valutare espressioni modello come STR."Il mio nome è \{nome}". Esistono processori di modelli dedicati come FMT, che è in grado di interpretare gli specificatori di formato. Per sapere come possono essere specificati i processori di template definiti dall'utente, dai un'occhiata più da vicino a JEP 430.

445: Unnamed Classes and Instance Main Methods (Preview)

Le classi senza nome e i metodi principali delle istanze servono a facilitare l'avvio degli studenti Java e a supportare gli insegnanti nell'introduzione graduale dei concetti di programmazione. Questo aggiornamento aiuta gli studenti a scrivere i loro primi programmi senza dover comprendere funzionalità linguistiche avanzate come classi e modificatori.

Le classi senza nome e i metodi principali dell'istanza consentono agli studenti di scrivere facilmente dichiarazioni semplificate per programmi a classe singola. È anche più semplice espandere i programmi in modo fluido man mano che le competenze e le esigenze evolvono. Senza questa funzionalità di anteprima, il tipico programma HelloWorld in Java appare come segue:

public class HelloWorld {
 public static void main(String[] args) {
  System.out.println("Hello, World!");
 }
}

Introducendo classi senza nome e metodi principali dell'istanza che non richiedono più String[] args come parametro, l'esempio precedente ora appare come segue:

void main() {
 System.out.println("Hello, World!");
}

Estensioni del codice multi-thread

Java 21 finalizza i thread virtuali, che semplificano la scalabilità delle tipiche architetture thread per richiesta su un numero teoricamente illimitato di thread. Per mantenere il loro impatto il più basso possibile, assicurati di controllare anche i valori per ambito, ma tieni presente che questi sono ancora in fase di preview e non ancora finalizzati.

Un'altra funzionalità di preview che aiuta a gestire numerosi thread virtuali è la concorrenza strutturata. Può essere utilizzato per coordinare correttamente e in modo robusto i thread virtuali e consente agli strumenti di osservabilità di visualizzare i thread così come vengono interpretati dallo sviluppatore.

444: Virtual Threads

Java ora introduce i thread virtuali con una mappatura m:n tra un thread virtuale e un thread del sistema operativo, il che significa che m thread virtuali possono essere eseguiti su n thread del sistema operativo. Questo nuovo approccio rende possibile creare, in teoria, quantità illimitate di thread virtuali, adattandosi ai requisiti delle odierne applicazioni server ad alto rendimento. Il diffuso stile thread per richiesta può essere utilizzato con i thread virtuali perché non esiste più un limite alla quantità di thread che possono essere creati.

I thread virtuali sono progettati in modo tale che le applicazioni esistenti possano essere portate per utilizzare thread virtuali con modifiche minime. Gli strumenti JDK esistenti possono essere utilizzati per risolvere i problemi, eseguire il debug e profilare i thread virtuali. È possibile creare un thread virtuale sulle API Thread, Thread.Builder o utilizzando java.util.concurrent.Executors.

Le modifiche rispetto alla seconda anteprima in JDK 20 includono:

- Supporto garantito per variabili thread-local.
- Ora viene fornito il monitoraggio della durata dei thread virtuali creati direttamente con l'API Thread.Builder. Tali thread possono essere osservati tramite un nuovo thread dump.

446: Scoped Values (Preview)

Questa funzionalità consente la condivisione di dati immutabili tra thread in un programma di grandi dimensioni senza utilizzare argomenti di metodo.

Le variabili locali del thread consentono già la condivisione di dati senza argomenti di metodo ma pongono vari difetti di progettazione:

- Sono mutabili e spesso portano a un flusso di dati simile a spaghetti difficile da leggere e comprendere.
- La loro durata è illimitata, il che comporta una durata di vita più lunga del necessario.
- Devono essere allocati nei thread figli ereditari (ovvero la memoria deve essere allocata alle variabili locali del thread), anche quando il thread figlio richiede solo l'accesso in lettura.
- Attraverso l'immutabilità dei valori con scope, vengono evitati gli svantaggi sopra menzionati delle variabili locali del thread. I valori con scope sono la scelta preferita rispetto alle variabili locali del thread, soprattutto nei casi in cui viene utilizzato un numero elevato di thread virtuali.

453: Structured Concurrency (Preview)

Questa funzionalità è stata introdotta in JDK 19 e JDK 20 e ora ha finalmente raggiunto la fase di preview in JDK 21. In sostanza, la concorrenza strutturata consente di trattare attività correlate eseguite in thread separati come una singola unità di lavoro.

L'obiettivo di questa funzionalità di anteprima è semplificare la programmazione multithread e incoraggiare gli sviluppatori ad applicare la programmazione simultanea. L'API di concorrenza strutturata migliora la gestione e l'annullamento degli errori, migliorando al tempo stesso l'affidabilità, la manutenibilità e l'osservabilità del codice.

Miglioramenti delle prestazioni e dell'infrastruttura

Java 21 apporta miglioramenti a Z Garbage Collector, un altro ciclo di incubazione di un'API per applicazioni vettoriali performanti e una preview di un'alternativa all'attuale interfaccia nativa Java (JNI).

439: Generational ZGC

Conosciuto da molti sviluppatori Java, Z Garbage Collector (ZGC) è un garbage collector a bassa latenza altamente scalabile. Java 21 estende ZGC introducendo la garbage collection generazionale per migliorare le prestazioni delle applicazioni.

Questo aggiornamento estende Z Garbage Collector (ZGC) per mantenere generazioni separate di oggetti (ad esempio oggetti giovani e vecchi). Con questo aggiornamento, ZGC sarà in grado di raccogliere oggetti giovani con maggiore frequenza, poiché hanno maggiori probabilità di morire giovani. Pertanto, l'utilizzo di ZGC generazionale può portare a una riduzione dei requisiti della CPU e del sovraccarico di memoria e può aiutare a evitare stalli nell'allocazione.

448: Vector API (6th incubator)

Vector API raggiunge la sesta fase di introduzione in JDK 21. Questa funzionalità è stata proposta per la prima volta in JDK 16, con ulteriori cicli di introduzione in ogni versione successiva. Le introduzioni vengono utilizzate per ottenere feedback anticipati dagli utenti sulle API non finali.

Questa API consente l'espressione di calcoli vettoriali che vengono compilati in modo affidabile in fase di runtime in istruzioni vettoriali ottimali sull'architettura della CPU utilizzata. Si tratta di un'API indipendente dalla piattaforma, chiara e concisa che aiuta a esprimere una varietà di calcoli vettoriali costituiti da sequenze di operazioni vettoriali composte all'interno di loop, possibilmente con flusso di controllo.

442: Foreign Function & Memory API (Third Preview)

L'API Foreign Function & Memory è stata presentata in anteprima per la prima volta in JDK 19, poi in JDK 20.

Questa nuova API migliora l'interoperabilità dei programmi Java con codice e dati esterni al runtime Java senza fare affidamento sulla Java Native Interface (JNI). Mira a sostituire del tutto JNI con una soluzione che sia più facile da usare, più sicura e offra prestazioni più elevate fornendo al contempo generalità consentendo alle app di operare su diversi tipi di memoria esterna.

L'API Foreign Function & Memory consente alle applicazioni Java di chiamare librerie native ed elaborare dati nativi richiamando funzioni esterne e accedendo in modo sicuro alla memoria esterna (ad esempio all'esterno della JVM). Attendiamo con ansia la prima versione ufficiale di questa funzionalità nelle future versioni di Java!

Altri miglioramenti

Se stai sviluppando algoritmi KEM (meccanismi di incapsulamento chiave), devi assolutamente dare un'occhiata alla nuova API Java. Inoltre, tieni d'occhio le due funzionalità che stanno per essere deprecate o pronte per essere rimosse del tutto!

Conclusione

Java 21 offre estensioni del linguaggio di base di cui ogni sviluppatore Java dovrebbe essere a conoscenza, dai modelli di record alle raccolte sequenziate ai modelli di stringhe. Finalizzando i thread virtuali e visualizzando in anteprima funzionalità aggiuntive attorno ad essi, gli sviluppatori Java hanno anche fatto un altro passo avanti nel rendere più semplice la vita degli sviluppatori che sviluppano codice multi-thread. Infine, anche Z Garbage Collector ha ricevuto alcuni miglioramenti e si continua a lavorare per fornire un'alternativa a JNI, che è ancora in fase di preview. 

Per approfondire l'argomento puoi visitare la pagina ufficiale.




Commenti
* Obbligatorio