Java 21 Record Patterns with Examples
Introduction
RecordsClick to read: Records make it easy to build an object from its components. Record patterns, previewed in Java 19 and 20 and final in Java 21Click to read tutorial: Overview of New Features in Java 21 (JEP 440), make it just as easy to take one apart: a single pattern tests whether an object is a given record and extracts its components into variables - and patterns can be nested to reach into records inside records. This tutorial explains record patterns with examples - deconstructing records in
OUTPUT of the above code
Nested patterns
The components of a record pattern are patterns themselves, so they can be record patterns too. This lets one pattern describe the shape of a whole data structure -
OUTPUT of the above code
Without record patterns, this needed a chain like
OUTPUT of the above code
This is the classic way to process tree-shaped data in functional languages, and records, sealed interfaces and patterns now make it natural in Java. Adding a new kind of expression, say
Generic records
For a generic record, the type arguments of a record pattern can be inferred -
OUTPUT of the above code
Note - Java 20's preview also allowed record patterns in enhanced
instanceof, nested patterns, using var, combining them with sealed interfaces and switch, and generic records.
Deconstructing a record
With pattern matching for instanceofClick to read: pattern matching for instanceof, you could test for a record type and then call its accessors. A record pattern lists the record's components in parentheses, and binds each to a variable directly -
Type pattern vs record pattern
//RecordPatternBasics.java
public class RecordPatternBasics {
record Point(int x, int y) { }
public static void main(String[] args) {
Object obj = new Point(3, 4);
if (obj instanceof Point p) { // Java 16: type pattern, then accessors
System.out.println("x + y = " + (p.x() + p.y()));
}
if (obj instanceof Point(int x, int y)) { // Java 21: record pattern
System.out.println("x + y = " + (x + y));
}
if (obj instanceof Point(var x, var y) && x == y) { // var infers the component types
System.out.println("on the diagonal");
} else {
System.out.println("not on the diagonal");
}
}
}
x + y = 7 x + y = 7 not on the diagonal
Point(int x, int y) matches if obj is a Point, and then calls the record's accessors to bind x and y. The components are listed in the record's declaration order, but the variable names are your own. A record pattern never matches null.
Reaching into records inside records
//NestedPatterns.java
public class NestedPatterns {
record Point(int x, int y) { }
enum Color { RED, GREEN }
record ColoredPoint(Point point, Color color) { }
record Line(ColoredPoint start, ColoredPoint end) { }
public static void main(String[] args) {
Object obj = new Line(new ColoredPoint(new Point(0, 0), Color.RED),
new ColoredPoint(new Point(3, 4), Color.GREEN));
if (obj instanceof Line(ColoredPoint(Point(var x1, var y1), Color c1),
ColoredPoint(Point(var x2, var y2), var c2))) {
double length = Math.hypot(x2 - x1, y2 - y1);
System.out.println("Line from " + c1 + " to " + c2 + ", length " + length);
}
}
}
Line from RED to GREEN, length 5.0
line.end().point().x() for every coordinate, plus null checks at each level. The nested pattern matches only if every level matches, so a null anywhere simply makes it fail.
Record patterns in switch
Record patterns are most powerful in switchClick to read tutorial: Java 21 Pattern Matching for switch with Examples, especially with a sealed interfaceClick to read: sealed interface: each case deconstructs one kind of record, and the compiler checks that every kind is handled -
Evaluating an expression tree
//ExpressionEvaluator.java
public class ExpressionEvaluator {
sealed interface Expr permits Num, Add, Mul, Neg { }
record Num(int value) implements Expr { }
record Add(Expr left, Expr right) implements Expr { }
record Mul(Expr left, Expr right) implements Expr { }
record Neg(Expr operand) implements Expr { }
static int eval(Expr e) {
return switch (e) {
case Num(int v) -> v;
case Add(Expr l, Expr r) -> eval(l) + eval(r);
case Mul(Num(int v), Expr r) when v == 0 -> 0; // nested pattern with a guard: skip work
case Mul(Expr l, Expr r) -> eval(l) * eval(r);
case Neg(Expr inner) -> -eval(inner);
}; // exhaustive: no default needed
}
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);
case Neg(var inner) -> "-" + show(inner);
};
}
public static void main(String[] args) {
Expr expr = new Add(new Mul(new Num(2), new Num(5)), new Neg(new Num(3)));
System.out.println(show(expr) + " = " + eval(expr));
Expr zero = new Mul(new Num(0), expr);
System.out.println(show(zero) + " = " + eval(zero));
}
}
(2 * 5 + -3) = 7 0 * (2 * 5 + -3) = 0
Div, makes both switches fail to compile until they handle it.
Record patterns with generic records
//GenericRecordPatterns.java
public class GenericRecordPatterns {
record Pair<A, B>(A first, B second) { }
static String describe(Pair<String, Integer> pair) {
if (pair instanceof Pair(var name, var age)) { // Pair<String, Integer> is inferred
return name + " is " + age;
}
return "?";
}
public static void main(String[] args) {
System.out.println(describe(new Pair<>("Tom", 30)));
Object o = new Pair<>("key", 42);
if (o instanceof Pair<?, ?>(String k, Integer v)) { // test the component types too
System.out.println(k + " -> " + (v + 1));
}
}
}
Tom is 30 key -> 43
for loops; that part was dropped before the feature became final in Java 21. Java 22 added the unnamed pattern _Click to read: unnamed pattern _ for components you do not need, as in Point(var x, _).
Summary
In this tutorial we saw how record patterns, final in Java 21, test for a record type and extract its components in one step. We deconstructed a record in instanceof, used var for the component types, nested patterns to reach into records inside records, and combined record patterns with a sealed interface and switch to evaluate an expression tree exhaustively. Lastly, we used record patterns with generic records. Next, read about pattern matching for switchClick to read tutorial: Java 21 Pattern Matching for switch with Examples.Continue Reading Java 18 to 21 Tutorials
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Sequenced Collections ExplainedClick to read tutorial: Java 21 Sequenced Collections Explained Java 21 Generational ZGC ExplainedClick to read tutorial: Java 21 Generational ZGC Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Sequenced Collections ExplainedClick to read tutorial: Java 21 Sequenced Collections Explained Java 21 Generational ZGC ExplainedClick to read tutorial: Java 21 Generational ZGC Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20