Ce face Record patterns
Un record pattern arată ca apelul constructorului, dar face invers: Punct(int x, int y) testează dacă obiectul e un Punct și, dacă da, pune componentele în x și y. Apare oriunde apar pattern-uri: în instanceof și în case.
Tiparele se pot imbrica: Linie(Punct(var x1, var y1), Punct(var x2, var y2)) desface două niveluri dintr-o dată. În locul tipului poți scrie var, iar pentru componentele care nu te interesează folosești _ (Java 22), care nu declară nicio variabilă.
Împreună cu interfețele sealed, record patterns permit un stil „date + funcții”: definești variantele ca records și scrii operațiile ca switch-uri complete, verificate de compilator. Ca în orice switch cu tipare, poți adăuga condiții cu when.
Exemple
Exemplul 1: Desfacere în instanceof și în switch
Componentele x și y sunt disponibile direct, fără apeluri p.x(). În switch, condiția when folosește variabilele extrase.
record Punct(int x, int y) { }
public class Main {
static String cadran(Object o) {
return switch (o) {
case Punct(int x, int y) when x == 0 && y == 0 -> "originea";
case Punct(int x, int y) when x > 0 && y > 0 -> "cadranul I";
case Punct(int x, int y) when x < 0 && y > 0 -> "cadranul II";
case Punct(int x, int y) when x < 0 && y < 0 -> "cadranul III";
case Punct(int x, int y) when x > 0 && y < 0 -> "cadranul IV";
case Punct p -> "pe o axă";
default -> "nu e punct";
};
}
public static void main(String[] args) {
Object o = new Punct(3, -4);
if (o instanceof Punct(int x, int y)) {
System.out.println("distanța de la origine: " + Math.sqrt(x * x + y * y));
}
Object[] puncte = {new Punct(0, 0), new Punct(2, 5), new Punct(-1, 7), new Punct(-2, -2), o, new Punct(0, 9), "Iași"};
for (Object p : puncte) {
System.out.println(p + " -> " + cadran(p));
}
}
}
Rezultat
distanța de la origine: 5.0
Punct[x=0, y=0] -> originea
Punct[x=2, y=5] -> cadranul I
Punct[x=-1, y=7] -> cadranul II
Punct[x=-2, y=-2] -> cadranul III
Punct[x=3, y=-4] -> cadranul IV
Punct[x=0, y=9] -> pe o axă
Iași -> nu e punct
Exemplul 2: Tipare imbricate, var și _
Comenzile conțin un client și o adresă. Switch-ul desface două niveluri; componentele nefolosite sunt marcate cu _.
sealed interface Livrare { }
record Adresa(String oras, String strada) { }
record Curier(String client, Adresa adresa, double greutate) implements Livrare { }
record Ridicare(String client, String magazin) implements Livrare { }
public class Main {
static String eticheta(Livrare l) {
return switch (l) {
case Curier(var client, Adresa(var oras, _), var kg) when kg > 30 ->
"marfă grea pentru " + client + " (" + oras + "), " + kg + " kg";
case Curier(var client, Adresa(var oras, var strada), _) ->
"curier: " + client + ", " + strada + ", " + oras;
case Ridicare(var client, var magazin) ->
client + " ridică din magazinul " + magazin;
};
}
public static void main(String[] args) {
Livrare[] livrari = {
new Curier("Ana Pop", new Adresa("Iași", "Str. Lăpușneanu 12"), 2.5),
new Curier("Ion Rusu", new Adresa("Galați", "Str. Brăilei 5"), 45),
new Ridicare("Maria Ene", "Palas"),
};
for (Livrare l : livrari) {
System.out.println(eticheta(l));
}
}
}
Rezultat
curier: Ana Pop, Str. Lăpușneanu 12, Iași
marfă grea pentru Ion Rusu (Galați), 45.0 kg
Maria Ene ridică din magazinul Palas
Exemplul 3: Calcul recursiv pe o expresie
O expresie aritmetică este fie un număr, fie o sumă, fie un produs. Funcția evalueaza desface fiecare nod și se apelează recursiv pe părți.
sealed interface Expr { }
record Numar(int valoare) implements Expr { }
record Suma(Expr stanga, Expr dreapta) implements Expr { }
record Produs(Expr stanga, Expr dreapta) implements Expr { }
public class Main {
static int evalueaza(Expr e) {
return switch (e) {
case Numar(int v) -> v;
case Suma(Expr a, Expr b) -> evalueaza(a) + evalueaza(b);
case Produs(Expr a, Expr b) -> evalueaza(a) * evalueaza(b);
};
}
static String text(Expr e) {
return switch (e) {
case Numar(int v) -> String.valueOf(v);
case Suma(Expr a, Expr b) -> "(" + text(a) + " + " + text(b) + ")";
case Produs(Expr a, Expr b) -> text(a) + " * " + text(b);
};
}
public static void main(String[] args) {
Expr e = new Produs(new Suma(new Numar(2), new Numar(3)), new Numar(4));
System.out.println(text(e) + " = " + evalueaza(e));
}
}
Rezultat
(2 + 3) * 4 = 20
Greșeli frecvente
- Pui alt număr de componente decât are record-ul:
incorrect number of nested patterns. - Scrii un tip greșit pentru o componentă (
Punct(double x, double y)pentru componenteint). Folosește tipul exact sauvar. - Folosești record patterns pe o clasă obișnuită. Desfacerea funcționează doar pentru records.
Comenzi înrudite
record: record declară o clasă imutabilă pentru date: constructorul, accesorii, equals, hashCode și toString sunt generați automat.Pattern matching (instanceof și switch): Pattern matching testează tipul (și forma) unui obiect și îi extrage părțile într-un singur pas, în instanceof sau în switch.sealed, permits și non-sealed: O clasă sau interfață sealed își alege explicit subtipurile (permits); fiecare subtip este final, sealed sau non-sealed.instanceof: instanceof verifică dacă un obiect este de un anumit tip; în forma nouă declară și o variabilă deja convertită la acel tip.