CodeGym /Corsi /JAVA 25 SELF /Record Patterns (Java 21+): sintassi ed esempi

Record Patterns (Java 21+): sintassi ed esempi

JAVA 25 SELF
Livello 65, Lezione 3
Disponibile

1. Approfondiamo le classi record

Prima di tuffarci nei record patterns, rivediamo che cos'è una classe record.

Record — è una classe compatta e immutabile con metodi equals, hashCode, toString generati automaticamente, oltre alla creazione automatica dei getter per tutti i componenti. Le classi record sono arrivate in Java 16 e sono state una vera manna per chi era stanco di scrivere manualmente i DTO.

// Classico: Point con due campi
record Point(int x, int y) {}

Creazione dell'oggetto e accesso ai campi:

Point p = new Point(10, 20);
System.out.println(p.x()); // 10
System.out.println(p.y()); // 20
System.out.println(p);     // Point[x=10, y=20]

Perché servono i record patterns

Fino a Java 21, se volevi verificare che un oggetto fosse un record e ottenerne i campi, bisognava scrivere molto codice manuale: verifica del tipo con instanceof, cast e chiamata dei getter.

Object obj = new Point(5, 7);
if (obj instanceof Point) {
    Point p = (Point) obj;
    int x = p.x();
    int y = p.y();
    System.out.println("x=" + x + ", y=" + y);
}

Se il record è complesso o annidato, il codice si trasforma rapidamente in una “giungla di estrazioni”. E usarlo dentro uno switch era scomodo.

I record patterns permettono di decomporre i valori delle classi record direttamente nel pattern matching: sia in instanceof sia in switch. Questo rende il codice più corto, più sicuro e più chiaro.

2. Sintassi dei record patterns: l'esempio più semplice

record Point(int x, int y) {}

Object obj = new Point(10, 20);

if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);
}

Che cosa succede qui?

  • obj instanceof Point(int x, int y) — verifichiamo il tipo e decomponiamo subito i componenti nelle variabili x e y.
  • Le variabili x e y sono disponibili solo all'interno del blocco if.

Risultato:

x=10, y=20

Attenzione: se obj non è un Point oppure è null, il blocco if non verrà eseguito e le variabili non saranno dichiarate.

3. Record patterns in switch: concisione e sicurezza

record Point(int x, int y) {}
record Circle(Point center, int radius) {}

Object shape = new Point(3, 4);

switch (shape) {
    case Point(int x, int y) -> System.out.println("Point: x=" + x + ", y=" + y);
    case Circle(Point center, int r) -> System.out.println("Circle with radius " + r);
    default -> System.out.println("Unknown shape!");
}
  • Nel case Point(int x, int y) otteniamo subito accesso a x e y.
  • Nel case Circle(Point center, int r) sono disponibili center e r.

Risultato:

Point: x=3, y=4

Con gerarchie sealed lo switch diventa esaustivo: se dimentichi di gestire un caso, il compilatore ti avvertirà.

4. Pattern annidati per record: decomposizione annidata

record Point(int x, int y) {}
record Line(Point start, Point end) {}

Object obj = new Line(new Point(1, 2), new Point(3, 4));

if (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
    System.out.println("Line from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
}

Risultato:

Line from (1,2) to (3,4)

Lavori con alberi, grafi e strutture complesse? I pattern annidati sono i tuoi alleati.

5. Record patterns con guard (when)

Object obj = new Point(0, 100);

if (obj instanceof Point(int x, int y) && x == 0) {
    System.out.println("Il punto è sull'asse Y: y=" + y);
}

In uno switch si fa con when:

switch (obj) {
    case Point(int x, int y) when x == 0 -> System.out.println("Sull'asse Y: y=" + y);
    case Point(int x, int y) when y == 0 -> System.out.println("Sull'asse X: x=" + x);
    case Point(int x, int y) -> System.out.println("Punto normale");
    default -> System.out.println("Non è un punto");
}

6. Applicazione dei record patterns in un'applicazione reale

Prendiamo un esempio pratico — figure geometriche.

record Point(int x, int y) {}
record Circle(Point center, int radius) {}
record Rectangle(Point topLeft, int width, int height) {}
public static void printShapeInfo(Object shape) {
    switch (shape) {
        case Point(int x, int y) -> System.out.println("Point: (" + x + ", " + y + ")");
        case Circle(Point(int x, int y), int radius) ->
            System.out.println("Circle: center=(" + x + ", " + y + "), radius=" + radius);
        case Rectangle(Point(int x, int y), int width, int height) ->
            System.out.println("Rectangle: topLeft=(" + x + ", " + y + "), size=" + width + "x" + height);
        default -> System.out.println("Unknown shape");
    }
}

Esempio di chiamata:

printShapeInfo(new Rectangle(new Point(5, 10), 20, 30));

Risultato:

Rectangle: topLeft=(5, 10), size=20x30

7. Limitazioni e particolarità d'uso dei record patterns

Solo per classi record. I record patterns funzionano solo con oggetti che sono effettivamente classi record.

class NotARecord {
    int a, b;
    NotARecord(int a, int b) { this.a = a; this.b = b; }
}

// Errore! Non è un record
// if (obj instanceof NotARecord(int a, int b)) { ... }

Corrispondenza per numero e tipo dei componenti. Il pattern deve corrispondere ai componenti della classe record.

record Pair(int a, String b) {}

// Errore: i tipi non corrispondono
// if (obj instanceof Pair(String a, int b)) { ... }

Le variabili sono disponibili solo all'interno del blocco. I nomi dichiarati nel pattern sono visibili nel corpo dello specifico if o case in cui il matching ha successo.

8. Pattern annidati: esempio con un albero

sealed interface Expr permits NumberExpr, PlusExpr, MinusExpr {}

record NumberExpr(int value) implements Expr {}
record PlusExpr(Expr left, Expr right) implements Expr {}
record MinusExpr(Expr left, Expr right) implements Expr {}
int eval(Expr expr) {
    return switch (expr) {
        case NumberExpr(int value) -> value;
        case PlusExpr(Expr left, Expr right) -> eval(left) + eval(right);
        case MinusExpr(Expr left, Expr right) -> eval(left) - eval(right);
    };
}

Esempio d'uso:

Expr e = new PlusExpr(new NumberExpr(7), new MinusExpr(new NumberExpr(10), new NumberExpr(3)));
System.out.println(eval(e)); // 7 + (10 - 3) = 14

Qui decomponiamo direttamente NumberExpr(int value), PlusExpr(Expr left, Expr right) e MinusExpr(Expr left, Expr right) — il codice diventa conciso ed espressivo.

9. Tabella: confronto tra pattern matching con e senza record patterns

Approccio Cast manuale del tipo Pattern matching con record patterns
Verifica del tipo
if (obj instanceof ...)
if (obj instanceof Point(int x, int y))
Cast del tipo
(Point) obj
Non necessario
Accesso ai campi
p.x(), p.y()
x, y
Decomposizione annidata Molto codice manuale Pattern annidati
Uso in switch Scomodo/impossibile Facile e conciso

10. Errori tipici nell'uso dei record patterns

Errore n. 1: tentare di applicare un record pattern a una classe non record. Se scrivi una classe normale e poi provi a “decomporla” come un record, il compilatore non lo permetterà. I record patterns funzionano solo con vere classi record.

Errore n. 2: numero o tipo di componenti non corrispondente. Il pattern deve corrispondere esattamente alla firma dei componenti del record. Per esempio, per record Pair(int a, String b) non puoi scrivere Pair(int x, int y).

Errore n. 3: provare a usare le variabili del pattern fuori dal blocco. I nomi dichiarati nel pattern sono disponibili solo all'interno del relativo if/case. Fuori dal blocco tali variabili non esistono.

Errore n. 4: usare i record patterns su vecchie versioni di JDK. Il supporto è arrivato in Java 21. Su Java 17 e precedenti otterrai un errore di sintassi. Verifica la versione di JDK e il supporto nell'IDE.

Errore n. 5: pattern annidati troppo profondi. Le possibilità di annidamento sono potenti, ma non complicare senza necessità: una struttura troppo profonda peggiora la leggibilità e aumenta il rischio di errori.

1
Compito
JAVA 25 SELF, livello 65, lezione 3
Bloccato
Editor geometrico: descrizione della posizione dei punti 📍
Editor geometrico: descrizione della posizione dei punti 📍
1
Compito
JAVA 25 SELF, livello 65, lezione 3
Bloccato
Il cervello del calcolatore: valutazione di espressioni complesse 🧠➕➖
Il cervello del calcolatore: valutazione di espressioni complesse 🧠➕➖
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION