Articoli approfonditi sulla tecnologia che plasma il futuro.

Java continua a migliorare e nessuno se ne accorge

La cadenza di rilascio semestrale di Java ha trasformato il linguaggio in silenzio. Pattern matching, virtual threads e record lo rendono irriconoscibile.

Una tazza di caffè luminosa mentre un vecchio ufficio spento si trasforma in uno spazio moderno e luminoso

Java è il Rodney Dangerfield dei linguaggi di programmazione: non ottiene mai il rispetto che merita. Nomina Java alla maggior parte degli sviluppatori sotto i 30 anni e immagineranno file di configurazione XML, enterprise bean e AbstractSingletonProxyFactoryBean. La reputazione del linguaggio è ferma al 2008, quando Java 6 era l'ultima versione e scrivere Java voleva dire annegare nel boilerplate.

Ma qualcosa di notevole è successo da quando Java è passato a una cadenza di rilascio semestrale nel 2017. Il linguaggio sta migliorando a un ritmo impensabile durante i quattro anni che separavano Java 6, 7 e 8. Record. Sealed class. Pattern matching. Virtual threads. String template. Structured concurrency. Ogni release aggiunge funzionalità che rendono il codice Java più corto, più espressivo e più piacevole da scrivere. Java 26 continua questa serie, e il divario tra «Java com'è» e «Java come la gente lo immagina» non è mai stato così ampio.

Le funzionalità che hanno cambiato tutto

Se non scrivi Java dai tempi di Java 8, ti stai perdendo un decennio di miglioramenti. Ecco com'è oggi il linguaggio.

Record (Java 14) sono classi dati immutabili. Quello che un tempo richiedeva 50 righe di boilerplate — campi, costruttore, getter, equals, hashCode, toString — oggi si riduce a una sola riga.

// Java 8: 50 lines of boilerplate
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() { return Objects.hash(x, y); }
@Override
public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
// Java 14+: one line
record Point(int x, int y) {}

Pattern matching (Java 16-21, in continua evoluzione) ti permette di destrutturare oggetti nelle espressioni switch e nei controlli instanceof. Questo elimina le catene a cascata di if-else-instanceof che affliggevano il codice Java.

// Pattern matching with sealed types and records
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Exhaustive pattern matching — compiler verifies all cases covered
double area(Shape shape) {
return switch (shape) {
case Circle(var r) -> Math.PI * r * r;
case Rectangle(var w, var h) -> w * h;
case Triangle(var b, var h) -> 0.5 * b * h;
};
}

Virtual threads (Java 21) sono thread leggeri gestiti dalla JVM invece che dal sistema operativo. Puoi crearne milioni senza esaurire la memoria o gli handle dei thread del sistema operativo. Questo rende il modello «un thread per richiesta», il più semplice dei modelli di concorrenza, praticabile su larga scala.

// Create a million concurrent tasks — try this with OS threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i -> {
executor.submit(() -> {
// Each task gets its own virtual thread
// Blocking I/O (HTTP, DB, file) automatically yields
var response = httpClient.send(request, BodyHandlers.ofString());
return response.body();
});
});
}
// All done — executor auto-closes, waits for completion

I virtual threads sono la risposta di Java alle goroutine di Go e alle coroutine di Kotlin. La differenza: non serve una sintassi speciale né «colorare» le funzioni. Il codice bloccante tradizionale funziona — la JVM sospende e riprende i virtual thread nei punti di blocco in modo trasparente. Il tuo client HTTP bloccante, il driver JDBC e il codice di I/O su file ne beneficiano senza modifiche.

Perché nessuno se ne accorge

Il percorso di miglioramento di Java è davvero impressionante. Allora perché la percezione è ancora ferma al 2008?

Primo, l'inerzia delle aziende. Molte organizzazioni usano Java 8 o 11 perché aggiornare una codebase grande è costoso e rischioso. Quando gli sviluppatori parlano di «Java», si riferiscono alla versione usata dalla loro azienda, non all'ultima release. Gli sviluppatori di Java 8 non hanno mai sperimentato record, pattern matching o virtual threads.

Secondo, il livello dei framework. Spring Boot, Jakarta EE e altri framework enterprise aggiungono la propria complessità sopra il linguaggio. Gli sviluppatori attribuiscono a «Java» la cerimonia delle annotation di Spring, ma è colpa del framework, non del linguaggio. Java moderno senza un framework pesante è sorprendentemente snello.

Terzo, l'inerzia culturale. La cultura tech assegna i linguaggi a delle tribù. Java è il linguaggio «enterprise», associato a grandi aziende, processi burocratici e over-engineering. Questa reputazione è stata meritata: lo sviluppo Java intorno al 2005 meritava le critiche. Ma le reputazioni sopravvivono di anni alle proprie cause.

Il vantaggio della JVM

I miglioramenti del linguaggio contano, ma il vantaggio più profondo di Java è ancora la JVM. Dopo tre decenni di ottimizzazioni, la JVM HotSpot è uno degli ambienti di runtime più sofisticati mai costruiti.

  • Compilazione JIT. La JVM profila il codice in esecuzione e compila i metodi «caldi» in codice nativo ottimizzato. Per le applicazioni a lunga esecuzione (server, elaborazione dati) le prestazioni spesso eguagliano o superano il C++, perché il JIT può ottimizzare in base al comportamento reale a runtime, non solo all'analisi statica.
  • Garbage collection. Le implementazioni moderne di GC (ZGC, Shenandoah) garantiscono pause inferiori al millisecondo anche con heap da terabyte. La critica secondo cui «le pause del GC uccidono la latenza» non è vera da anni: questi collector girano in concorrenza con l'applicazione.
  • Osservabilità. JFR (Java Flight Recorder) offre profiling sicuro in produzione con un overhead quasi nullo. Puoi analizzare utilizzo della CPU, allocazione di memoria, contesa dei lock, latenza di I/O e comportamento del GC senza rallentare l'applicazione.
  • Maturità dell'ecosistema. Maven Central ha milioni di pacchetti. Gli strumenti di build (Gradle, Maven) sono ben conosciuti. L'ecosistema di test (JUnit, Mockito, Testcontainers) è eccellente. La storia del deployment (container, immagini native GraalVM) è maturata notevolmente.

GraalVM e le immagini native

Il punto debole storico di Java è sempre stato il tempo di avvio. La JVM impiega centinaia di millisecondi per inizializzarsi, caricare le classi e scaldare il compilatore JIT. Per le funzioni serverless e gli strumenti CLI, questo overhead è inaccettabile.

GraalVM Native Image risolve il problema compilando in anticipo le applicazioni Java in eseguibili standalone. Non serve la JVM. Il tempo di avvio scende da centinaia di millisecondi a poche unità di millisecondi. Anche l'uso di memoria diminuisce, perché non ci sono interprete, compilatore JIT e metadati delle classi da caricare.

Il compromesso: le immagini native perdono l'ottimizzazione adattiva della JVM. Un'applicazione compilata con il JIT diventa più veloce col tempo, man mano che la JVM impara i percorsi caldi. Un'immagine nativa parte veloce ma non migliora. Per i processi di breve durata (strumenti CLI, serverless) le immagini native vincono. Per i server a lunga esecuzione, il JIT della JVM alla fine produce codice più veloce. Scegli lo strumento giusto in base alle caratteristiche del tuo runtime.

Java moderno contro le alternative

I linguaggi che avrebbero «sostituito» Java — Go, Kotlin, Rust — sono ottimi. Ma Java moderno compete più efficacemente di quanto si pensi.

La semplicità di Go è attraente, ma Java ora ha i virtual threads (equivalenti alle goroutine) e un sistema di tipi molto più ricco. I sistemi di tipi contano nelle codebase grandi, e quello di Java, soprattutto con i sealed type e il pattern matching, intercetta errori che il sistema di interfacce di Go non vede.

Kotlin migliora la sintassi di Java ma gira sulla stessa JVM. La maggior parte dei vantaggi di Kotlin su Java 8 (null safety, data class, coroutine) ha equivalenti parziali o completi in Java moderno (pattern Optional, record, virtual threads). Kotlin resta più conciso, ma il divario si è ridotto parecchio.

Rust occupa una nicchia diversa: programmazione di sistema senza garbage collector. Java non dovrebbe competere lì. Ma per le applicazioni server-side, l'elaborazione dati e il software enterprise, la combinazione di prestazioni, sicurezza e maturità dell'ecosistema di Java è difficile da battere.

La cadenza semestrale è la vera innovazione

Più di ogni singola funzionalità, il ciclo di rilascio semestrale ha cambiato la traiettoria di Java. Prima del 2017, le release di Java erano eventi ogni tre-quattro anni che cercavano di consegnare tutto in una volta. Le funzionalità venivano rinviate perché non erano pronte, il che ritardava l'intera release, che creava pressione per spedire tutto nella successiva, che a sua volta slittava. Java 9 ha impiegato più di tre anni, in parte per la complessità del sistema dei moduli.

Con i rilasci semestrali, le funzionalità vengono consegnate quando sono pronte. Il pattern matching è stato introdotto in modo incrementale tra Java 16 e 24, con ogni release che aggiunge un pezzo. I virtual threads sono passati attraverso due release in preview prima della versione finale. Questo approccio incrementale permette al team di Java di raccogliere feedback reali e correggere i design prima di prendere decisioni API definitive.

La lezione vale anche al di là di Java: piccole release frequenti si accumulano in un cambiamento trasformativo. Ogni singola release di Java dalla 17 in poi è stata modesta. Prese insieme, hanno reinventato il linguaggio. Se hai provato Java cinque anni fa e ti sei scoraggiato per la verbosità, riprova. Potresti restare sorpreso da quello che trovi.