Record Patterns

Deconstructing Records (Java 21)

A record pattern takes a record apart into its components right in instanceof or case, and patterns can be nested.

Example

An expression evaluator — the classic use case for sealed interfaces + record patterns.

public class Main {
    static int eval(Expr e) {
        return switch (e) {
            case Num(int value) -> value;
            case Add(Expr left, Expr right) -> eval(left) + eval(right);
            case Mul(Num(int a), Num(int b)) -> a * b;              // nested pattern: fast path
            case Mul(Expr left, Expr right) -> eval(left) * eval(right);
        };
    }

    static String show(Expr e) {
        return switch (e) {
            case Num(var v) -> String.valueOf(v);
            case Add(var l, var r) -> "(" + show(l) + " + " + show(r) + ")";
            case Mul(var l, var r) -> show(l) + " * " + show(r);
        };
    }

    public static void main(String[] args) {
        Expr e = new Add(new Num(2), new Mul(new Num(3), new Num(4)));
        System.out.println(show(e) + " = " + eval(e));

        Object point = new Point(3, 4);
        if (point instanceof Point(int x, int y) && x > 0) {
            System.out.println("x=" + x + ", y=" + y + ", distance^2=" + (x * x + y * y));
        }
    }
}

sealed interface Expr permits Num, Add, Mul {}
record Num(int value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}
record Point(int x, int y) {}

Output

(2 + 3 * 4) = 14
x=3, y=4, distance^2=25

Feature Timeline

VersionFeature
Java 10var
Java 14switch expressions, helpful NullPointerException messages
Java 15text blocks
Java 16records, pattern matching for instanceof, Stream.toList()
Java 17 (LTS)sealed classes
Java 21 (LTS)pattern matching for switch, record patterns, virtual threads, sequenced collections
Java 22+unnamed variables _, statements before super(), stream gatherers, module imports