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

Basic.java
public class Basic {
    record Point(int x, int y) {}

    public static void main(String[] args) {
        Object obj = ;
        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 = ;
        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 = ;
        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));
        }
    }
}
record_pattern A record pattern destructures a record by matching its components directly in the pattern.

Nested Record Patterns

Nested.java
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 = ;
        double radius = ;
        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 = ;
        double radius = ;
        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 = ;
        double radius = ;
        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 = ;
        double radius = ;
        Circle circle = new Circle(new Point(centerX, 20), radius);
        describe(circle);
    }
}
nested_pattern Record patterns can be nested to destructure deeply nested record structures in a single pattern.

Using var in Patterns

Var.java
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 = ;
        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 = ;
        checkAge(new Person("Alice", aliceAge, "NYC"));
        checkAge(new Person("Bob", 15, "LA"));
    }
}
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
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));
    }
}

Generic Record Patterns

Generics.java
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));
    }
}
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)