CodeGym /Kurse /JAVA 25 SELF /Record Patterns (Java 21+): Syntax, Beispiele

Record Patterns (Java 21+): Syntax, Beispiele

JAVA 25 SELF
Level 65 , Lektion 3
Verfügbar

1. Tiefer in Record-Klassen eintauchen

Bevor wir in die Record Patterns eintauchen, frischen wir kurz auf, was eine record-Klasse ist.

Record ist eine kompakte, unveränderliche Klasse mit automatisch generierten Methoden equals, hashCode, toString sowie mit automatisch erzeugten Gettern für alle Komponenten. Record-Klassen erschienen in Java 16 und sind ein echtes Geschenk für alle, die es leid waren, „DTOs“ von Hand zu schreiben.

// Klassiker: Point mit zwei Feldern
record Point(int x, int y) {}

Objekterzeugung und Zugriff auf Felder:

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]

Wozu Record Patterns gut sind

Bis Java 21 musste man, wenn man prüfen wollte, dass ein Objekt ein bestimmter record ist und seine Felder erhalten möchte, viel manuellen Code schreiben: Typprüfung über instanceof, Casting und Aufruf der 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);
}

Wenn der record komplex oder verschachtelt ist, wird der Code schnell zu einem „Entpackungswald“. Und solches Erkennen in switch zu verwenden, war früher umständlich.

Record Patterns erlauben, die Werte von record-Klassen direkt im Pattern Matching zu entpacken: sowohl in instanceof als auch in switch. Das macht den Code kürzer, sicherer und verständlicher.

2. Syntax der Record Patterns: das einfachste Beispiel

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);
}

Was passiert hier?

  • obj instanceof Point(int x, int y) – wir prüfen den Typ und entpacken die Komponenten sofort in die Variablen x und y.
  • Die Variablen x und y sind nur innerhalb des if-Blocks verfügbar.

Ergebnis:

x=10, y=20

Achtung: Wenn obj kein Point ist oder null ist, wird der if-Block nicht ausgeführt und die Variablen werden nicht deklariert.

3. Record Patterns in switch: Prägnanz und Sicherheit

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!");
}
  • Im case Point(int x, int y) erhalten wir sofort Zugriff auf x und y.
  • Im case Circle(Point center, int r) sind center und r verfügbar.

Ergebnis:

Point: x=3, y=4

Mit sealed-Hierarchien wird switch erschöpfend: Wenn ein Fall nicht behandelt wird, warnt der Compiler.

4. Verschachtelte Record Patterns: Entpacken innerhalb des Entpackens

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 + ")");
}

Ergebnis:

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

Arbeiten Sie mit Bäumen, Graphen, komplexen Strukturen? Verschachtelte Patterns sind Ihre Freunde.

5. Record Patterns mit Guard-Ausdrücken (when)

Object obj = new Point(0, 100);

if (obj instanceof Point(int x, int y) && x == 0) {
    System.out.println("Punkt liegt auf der Y-Achse: y=" + y);
}

In switch geht das mit when:

switch (obj) {
    case Point(int x, int y) when x == 0 -> System.out.println("Auf der Y-Achse: y=" + y);
    case Point(int x, int y) when y == 0 -> System.out.println("Auf der X-Achse: x=" + x);
    case Point(int x, int y) -> System.out.println("Gewöhnlicher Punkt");
    default -> System.out.println("Kein Punkt");
}

6. Einsatz von Record Patterns in einer realen Anwendung

Nehmen wir ein praktisches Beispiel – geometrische Formen.

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");
    }
}

Beispielaufruf:

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

Ergebnis:

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

7. Einschränkungen und Feinheiten bei der Verwendung von Record Patterns

Nur für Record-Klassen. Record Patterns funktionieren nur mit Objekten, die tatsächlich record-Klassen sind.

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

// Fehler! Kein Record
// if (obj instanceof NotARecord(int a, int b)) { ... }

Übereinstimmung der Komponenten in Anzahl und Typ. Das Pattern muss den Komponenten der Record-Klasse entsprechen.

record Pair(int a, String b) {}

// Fehler: Typen stimmen nicht überein
// if (obj instanceof Pair(String a, int b)) { ... }

Variablen sind nur innerhalb des Blocks verfügbar. Die im Pattern deklarierten Namen sind im Rumpf des jeweiligen if oder case sichtbar, in dem das Matching erfolgreich war.

8. Verschachtelte Patterns: Beispiel mit einem Baum

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);
    };
}

Beispielverwendung:

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

Hier packen wir NumberExpr(int value), PlusExpr(Expr left, Expr right) und MinusExpr(Expr left, Expr right) direkt aus – der Code wird knapp und ausdrucksstark.

9. Tabelle: Vergleich von Pattern Matching mit und ohne Record Patterns

Ansatz Manuelles Casting Pattern Matching mit Record Patterns
Typprüfung
if (obj instanceof ...)
if (obj instanceof Point(int x, int y))
Casting
(Point) obj
Nicht nötig
Zugriff auf Felder
p.x(), p.y()
x, y
Verschachteltes Entpacken Viel manueller Code Verschachtelte Patterns
Verwendung in switch Unkomfortabel/unmöglich Einfach, prägnant

10. Typische Fehler bei der Verwendung von Record Patterns

Fehler Nr. 1: Versuch, ein Record Pattern auf eine Nicht-Record-Klasse anzuwenden. Wenn Sie eine normale Klasse geschrieben haben und dann versuchen, sie wie einen record zu „entpacken“, lässt der Compiler das nicht zu. Record Patterns funktionieren nur mit echten record-Klassen.

Fehler Nr. 2: nicht übereinstimmende Anzahl oder Typen der Komponenten. Das Pattern muss der Signatur der Komponenten des record vollständig entsprechen. Für record Pair(int a, String b) darf man z. B. nicht Pair(int x, int y) schreiben.

Fehler Nr. 3: Versuch, Pattern-Variablen außerhalb des Blocks zu verwenden. Die im Pattern deklarierten Namen sind nur innerhalb des jeweiligen if/case sichtbar. Außerhalb des Blocks existieren solche Variablen nicht.

Fehler Nr. 4: Verwendung von Record Patterns auf älteren JDK-Versionen. Unterstützung erschien in Java 21. Unter Java 17 und darunter erhalten Sie einen Syntaxfehler. Prüfen Sie die JDK-Version und die Unterstützung in der IDE.

Fehler Nr. 5: übermäßig tief verschachtelte Patterns. Verschachtelungen sind mächtig, aber übertreiben Sie es nicht: Zu tiefe Strukturen verschlechtern die Lesbarkeit und erhöhen das Fehlerrisiko.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION