When working with structured data like API responses, events, or domain objects, you often need to extract nested fields. Record patterns let you destructure records directly in pattern matching, extracting component values without explicit accessor calls.

Basic Syntax

obj
Basic.java
Replay: real traced execution (multi-file project)
public class Basic {
    record Point(int x, int y) {}

    public static void main(String[] args) {
        Object obj = new Point(10, 20);
        if (obj instanceof Point(int x, int y)) {
            System.out.println("Point coordinates:");
            System.out.println("  x = " + x);
            System.out.println("  y = " + y);
            System.out.println("  distance from origin = " +
                Math.sqrt(x * x + y * y));
        }
    }
}
public class Basic {
    record Point(int x, int y) {}

    public static void main(String[] args) {
        Object obj = new Point(3, 4);
        if (obj instanceof Point(int x, int y)) {
            System.out.println("Point coordinates:");
            System.out.println("  x = " + x);
            System.out.println("  y = " + y);
            System.out.println("  distance from origin = " +
                Math.sqrt(x * x + y * y));
        }
    }
}
public class Basic {
    record Point(int x, int y) {}

    public static void main(String[] args) {
        Object obj = new Point(-5, 12);
        if (obj instanceof Point(int x, int y)) {
            System.out.println("Point coordinates:");
            System.out.println("  x = " + x);
            System.out.println("  y = " + y);
            System.out.println("  distance from origin = " +
                Math.sqrt(x * x + y * y));
        }
    }
}
  1. obj ← Point[x=10, y=20]

    4public static void main(String[] args) {5    Object obj→ Point[x=10, y=20] = new Point(10, 20); //@obj=new Point(10, 20), new Point(3, 4), new Point(-5, 12)6    if (obj instanceof Point(int x, int y)) {
  2. if (obj instanceof Point(int x, int y))

    5Object obj = new Point(10, 20); //@obj=new Point(10, 20), new Point(3, 4), new Point(-5, 12)6if (obj instanceof Point(int x, int y)) {7    System.out.println("Point coordinates:");8    System.out.println("  x = " + x10);9    System.out.println("  y = " + y20);10    System.out.println("  distance from origin = " + 11        Math.sqrt(x10 * x + y20 * y));12}
    outputPoint coordinates:
      x = 10
      y = 20
      distance from origin = 22.360679774997898
  1. obj ← Point[x=3, y=4]

    4public static void main(String[] args) {5    Object obj→ Point[x=3, y=4] = new Point(3, 4);6    if (obj instanceof Point(int x, int y)) {
  2. if (obj instanceof Point(int x, int y))

    5Object obj = new Point(3, 4);6if (obj instanceof Point(int x, int y)) {7    System.out.println("Point coordinates:");8    System.out.println("  x = " + x3);9    System.out.println("  y = " + y4);10    System.out.println("  distance from origin = " + 11        Math.sqrt(x3 * x + y4 * y));12}
    outputPoint coordinates:
      x = 3
      y = 4
      distance from origin = 5.0
  1. obj ← Point[x=-5, y=12]

    4public static void main(String[] args) {5    Object obj→ Point[x=-5, y=12] = new Point(-5, 12);6    if (obj instanceof Point(int x, int y)) {
  2. if (obj instanceof Point(int x, int y))

    5Object obj = new Point(-5, 12);6if (obj instanceof Point(int x, int y)) {7    System.out.println("Point coordinates:");8    System.out.println("  x = " + x-5);9    System.out.println("  y = " + y12);10    System.out.println("  distance from origin = " + 11        Math.sqrt(x-5 * x + y12 * y));12}
    outputPoint coordinates:
      x = -5
      y = 12
      distance from origin = 13.0
record_pattern A record pattern destructures a record by matching its components directly in the pattern.

Nested Record Patterns

example
Nested.java
Replay: real traced execution (multi-file project)
public class Nested {
    record Point(int x, int y) {}
    record Circle(Point center, double radius) {}

    static void describe(Object obj) {
        if (obj instanceof Circle(Point(int x, int y), double r)) {
            System.out.println("Circle:");
            System.out.println("  center: (" + x + ", " + y + ")");
            System.out.println("  radius: " + r);
        }
    }

    public static void main(String[] args) {
        int centerX = 10;
        double radius = 5.0;
        Circle circle = new Circle(new Point(centerX, 20), radius);
        describe(circle);
    }
}
public class Nested {
    record Point(int x, int y) {}
    record Circle(Point center, double radius) {}

    static void describe(Object obj) {
        if (obj instanceof Circle(Point(int x, int y), double r)) {
            System.out.println("Circle:");
            System.out.println("  center: (" + x + ", " + y + ")");
            System.out.println("  radius: " + r);
        }
    }

    public static void main(String[] args) {
        int centerX = 0;
        double radius = 5.0;
        Circle circle = new Circle(new Point(centerX, 20), radius);
        describe(circle);
    }
}
public class Nested {
    record Point(int x, int y) {}
    record Circle(Point center, double radius) {}

    static void describe(Object obj) {
        if (obj instanceof Circle(Point(int x, int y), double r)) {
            System.out.println("Circle:");
            System.out.println("  center: (" + x + ", " + y + ")");
            System.out.println("  radius: " + r);
        }
    }

    public static void main(String[] args) {
        int centerX = 10;
        double radius = 2.5;
        Circle circle = new Circle(new Point(centerX, 20), radius);
        describe(circle);
    }
}
  1. centerX ← 10, radius ← 5.0, circle ← Circle[center=Point[x=10, y=20], radius=5.0]

    13public static void main(String[] args) {14    int centerX→ 10 = 10; //@centerX=10, 015    double radius→ 5.0 = 5.0; //@radius=5.0, 2.516    Circle circle→ Circle[center=Point[x=10, y=20], radius=5.0] = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=10, y=20], radius=5.0]);18}
  2. static void describe(Object obj)

    5static void describe(Object objCircle[center=Point[x=10, y=20], radius=5.0]) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {
  3. if (obj instanceof Circle(Point(int x, int y), double r))

    5static void describe(Object obj) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {7        System.out.println("Circle:");8        System.out.println("  center: (" + x10 + ", " + y20 + ")");9        System.out.println("  radius: " + r5.0);10    }
    outputCircle:
      center: (10, 20)
      radius: 5.0
  4. describe(circle);

    16    Circle circle = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=10, y=20], radius=5.0]);18}
  1. centerX ← 0, radius ← 5.0, circle ← Circle[center=Point[x=0, y=20], radius=5.0]

    13public static void main(String[] args) {14    int centerX→ 0 = 0;15    double radius→ 5.0 = 5.0;16    Circle circle→ Circle[center=Point[x=0, y=20], radius=5.0] = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=0, y=20], radius=5.0]);18}
  2. static void describe(Object obj)

    5static void describe(Object objCircle[center=Point[x=0, y=20], radius=5.0]) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {
  3. if (obj instanceof Circle(Point(int x, int y), double r))

    5static void describe(Object obj) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {7        System.out.println("Circle:");8        System.out.println("  center: (" + x0 + ", " + y20 + ")");9        System.out.println("  radius: " + r5.0);10    }
    outputCircle:
      center: (0, 20)
      radius: 5.0
  4. describe(circle);

    16    Circle circle = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=0, y=20], radius=5.0]);18}
  1. centerX ← 10, radius ← 2.5, circle ← Circle[center=Point[x=10, y=20], radius=2.5]

    13public static void main(String[] args) {14    int centerX→ 10 = 10;15    double radius→ 2.5 = 2.5;16    Circle circle→ Circle[center=Point[x=10, y=20], radius=2.5] = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=10, y=20], radius=2.5]);18}
  2. static void describe(Object obj)

    5static void describe(Object objCircle[center=Point[x=10, y=20], radius=2.5]) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {
  3. if (obj instanceof Circle(Point(int x, int y), double r))

    5static void describe(Object obj) {6    if (obj instanceof Circle(Point(int x, int y), double r)) {7        System.out.println("Circle:");8        System.out.println("  center: (" + x10 + ", " + y20 + ")");9        System.out.println("  radius: " + r2.5);10    }
    outputCircle:
      center: (10, 20)
      radius: 2.5
  4. describe(circle);

    16    Circle circle = new Circle(new Point(centerX, 20), radius);17    describe(circleCircle[center=Point[x=10, y=20], radius=2.5]);18}
nested_pattern Record patterns can be nested to destructure deeply nested record structures in a single pattern.

Using var in Patterns

aliceAge
Var.java
Replay: real traced execution (multi-file project)
public class Var {
    record Person(String name, int age, String city) {}

    static void checkAge(Object obj) {
        if (obj instanceof Person(var name, int age, var city)) {
            if (age >= 18) {
                System.out.println(name + " is an adult");
            } else {
                System.out.println(name + " is a minor");
            }
        }
    }

    public static void main(String[] args) {
        int aliceAge = 25;
        checkAge(new Person("Alice", aliceAge, "NYC"));
        checkAge(new Person("Bob", 15, "LA"));
    }
}
public class Var {
    record Person(String name, int age, String city) {}

    static void checkAge(Object obj) {
        if (obj instanceof Person(var name, int age, var city)) {
            if (age >= 18) {
                System.out.println(name + " is an adult");
            } else {
                System.out.println(name + " is a minor");
            }
        }
    }

    public static void main(String[] args) {
        int aliceAge = 17;
        checkAge(new Person("Alice", aliceAge, "NYC"));
        checkAge(new Person("Bob", 15, "LA"));
    }
}
  1. aliceAge ← 25

    14public static void main(String[] args) {15    int aliceAge→ 25 = 25; //@aliceAge=25, 1716    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));
  2. static void checkAge(Object obj)

    pass 1 of 2
    4static void checkAge(Object objPerson[name=Alice, age=25, city=NYC]) {5    if (obj instanceof Person(var name, int age, var city)) {
  3. if (age >= 18)

    5if (obj instanceof Person(var name, int age, var city)) {6    if (age25 >= 18) {7        System.out.println(nameAlice + " is an adult");8    } else {
    outputAlice is an adult
  4. checkAge(new Person("Alice", aliceAge, "NYC"));

    15    int aliceAge = 25; //@aliceAge=25, 1716    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));18}
  5. static void checkAge(Object obj)

    pass 2 of 2
    4static void checkAge(Object objPerson[name=Bob, age=15, city=LA]) {5    if (obj instanceof Person(var name, int age, var city)) {
  6. else

    7    System.out.println(name + " is an adult");8} else {9    System.out.println(nameBob + " is a minor");10}
    outputBob is a minor
  7. checkAge(new Person("Bob", 15, "LA"));

    16    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));18}
  1. aliceAge ← 17

    14public static void main(String[] args) {15    int aliceAge→ 17 = 17;16    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));
  2. static void checkAge(Object obj)

    pass 1 of 2
    4static void checkAge(Object objPerson[name=Alice, age=17, city=NYC]) {5    if (obj instanceof Person(var name, int age, var city)) {
  3. else

    pass 1 of 2
    7    System.out.println(name + " is an adult");8} else {9    System.out.println(nameAlice + " is a minor");10}
    outputAlice is a minor
  4. checkAge(new Person("Alice", aliceAge, "NYC"));

    15    int aliceAge = 17;16    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));18}
  5. static void checkAge(Object obj)

    pass 2 of 2
    4static void checkAge(Object objPerson[name=Bob, age=15, city=LA]) {5    if (obj instanceof Person(var name, int age, var city)) {
  6. else

    pass 2 of 2
    7    System.out.println(name + " is an adult");8} else {9    System.out.println(nameBob + " is a minor");10}
    outputBob is a minor
  7. checkAge(new Person("Bob", 15, "LA"));

    16    checkAge(new Person("Alice", aliceAge, "NYC"));17    checkAge(new Person("Bob", 15, "LA"));18}
var_pattern Using `var` in record patterns lets the compiler infer component types, reducing verbosity.

Benefits

  • Destructuring: extract record components in one step
  • Concise code: no need to call accessor methods
  • Nested patterns: destructure nested records
  • Type-safe: compiler verifies component types

Record Patterns in Switch

Switch.java
Replay: real traced execution (multi-file project)
public class Switch {
    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 {}

    static double area(Shape shape) {
        return switch (shape) {
            case Circle(double r) -> Math.PI * r * r;
            case Rectangle(double w, double h) -> w * h;
            case Triangle(double b, double h) -> 0.5 * b * h;
        };
    }

    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rect = new Rectangle(4.0, 6.0);
        Shape tri = new Triangle(3.0, 4.0);

        System.out.println("Circle area: " + area(circle));
        System.out.println("Rectangle area: " + area(rect));
        System.out.println("Triangle area: " + area(tri));
    }
}
  1. circle ← Circle[radius=5.0], rect ← Rectangle[width=4.0, height=6.0]

    15public static void main(String[] args) {16    Shape circle→ Circle[radius=5.0] = new Circle(5.0);17    Shape rect→ Rectangle[width=4.0, height=6.0] = new Rectangle(4.0, 6.0);18    Shape tri→ Triangle[base=3.0, height=4.0] = new Triangle(3.0, 4.0);1920    System.out.println("Circle area: " + area(circleCircle[radius=5.0]));21    System.out.println("Rectangle area: " + area(rect));
  2. static double area(Shape shape)

    pass 1 of 3
    7static double area(Shape shapeCircle[radius=5.0]) {8    return switch (shape) {9        case Circle(double r) -> Math.PI * r * r;10        case Rectangle(double w, double h) -> w * h;11        case Triangle(double b, double h) -> 0.5 * b * h;12    };13}
    All 3 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Triangle[base=3.0, height=4.0]
  3. System.out.println("Circle area: " + area(circle));

    20System.out.println("Circle area: " + area(circleCircle[radius=5.0]));21System.out.println("Rectangle area: " + area(rectRectangle[width=4.0, height=6.0]));22System.out.println("Triangle area: " + area(tri));
    outputCircle area: 78.53981633974483
  4. System.out.println("Rectangle area: " + area(rect));

    20    System.out.println("Circle area: " + area(circle));21    System.out.println("Rectangle area: " + area(rectRectangle[width=4.0, height=6.0]));22    System.out.println("Triangle area: " + area(triTriangle[base=3.0, height=4.0]));23}
    outputRectangle area: 24.0
  5. System.out.println("Triangle area: " + area(tri));

    21    System.out.println("Rectangle area: " + area(rect));22    System.out.println("Triangle area: " + area(triTriangle[base=3.0, height=4.0]));23}
    outputTriangle area: 6.0

Generic Record Patterns

Generics.java
Replay: real traced execution (multi-file project)
import java.util.Optional;

public class Generics {
    record Box<T>(T value) {}

    static void unwrap(Object obj) {
        if (obj instanceof Box(String s)) {
            System.out.println("Box contains string: " + s);
        } else if (obj instanceof Box(Integer i)) {
            System.out.println("Box contains integer: " + i);
        } else if (obj instanceof Box<?> b) {
            System.out.println("Box contains: " + b.value());
        }
    }

    public static void main(String[] args) {
        unwrap(new Box<>("hello"));
        unwrap(new Box<>(42));
        unwrap(new Box<>(3.14));
    }
}
  1. public static void main(String[] args)

    16public static void main(String[] args) {17    unwrap(new Box<>("hello"));18    unwrap(new Box<>(42));
  2. static void unwrap(Object obj)

    pass 1 of 3
    6static void unwrap(Object objBox[value=hello]) {7    if (obj instanceof Box(String s)) {
    All 3 passes — pass 1 is the card above
    passobjsi
    1Box[value=hello]hello
    2Box[value=42]42
    3Box[value=3.14]
  3. if (obj instanceof Box(String s))

    6static void unwrap(Object obj) {7    if (obj instanceof Box(String s)) {8        System.out.println("Box contains string: " + shello);9    } else if (obj instanceof Box(Integer i)) {
    outputBox contains string: hello
  4. unwrap(new Box<>("hello"));

    16public static void main(String[] args) {17    unwrap(new Box<>("hello"));18    unwrap(new Box<>(42));19    unwrap(new Box<>(3.14));
  5. if (obj instanceof Box(Integer i))

    8    System.out.println("Box contains string: " + s);9} else if (obj instanceof Box(Integer i)) {10    System.out.println("Box contains integer: " + i42);11} else if (obj instanceof Box<?> b) {
    outputBox contains integer: 42
  6. unwrap(new Box<>(42));

    17    unwrap(new Box<>("hello"));18    unwrap(new Box<>(42));19    unwrap(new Box<>(3.14));20}
  7. if (obj instanceof Box<?> b)

    10    System.out.println("Box contains integer: " + i);11} else if (obj instanceof Box<?> b) {12    System.out.println("Box contains: " + b.value());13}
    outputBox contains: 3.14
  8. unwrap(new Box<>(3.14));

    18    unwrap(new Box<>(42));19    unwrap(new Box<>(3.14));20}
generic_pattern Record patterns work with generic records, preserving type safety for parameterized components.

Exercise: Practical.java

Create a JSON-like data processor that handles nested Value records (StringValue, NumberValue, ObjectValue, ArrayValue)