skip to content

What is a record deconstruction pattern in Java, and how does it differ from a plain type pattern?

level: juniorimportance: should knowfreq 55%

answer

  1. Type pattern binds the whole object; record pattern binds the parts
  2. Shape mirrors the canonical constructor (construction in reverse)
  3. Sub-patterns, one per component, in declaration order
  4. Works in instanceof and switch; record type required
  5. Calls accessors under the hood

basics

~20 s

A record pattern matches a record and pulls its components into variables in one step. Instead of binding the whole record like 'Point p', you write 'Point(int x, int y)' to get x and y directly without calling p.x() and p.y().

solid answer

~40 s

A record deconstruction pattern tests whether a value is a given record type and, if so, binds each component to a variable in a single step. A plain type pattern like 'obj instanceof Point p' binds the whole object to 'p', so you still call accessors (p.x(), p.y()) to read the parts. A record pattern, 'obj instanceof Point(int x, int y)', goes further: it checks the type and then extracts the components by invoking their accessors under the hood, binding x and y for you. The shape mirrors the record's canonical constructor, so the pattern reads like the construction in reverse. Record patterns work in both 'instanceof' and 'switch'. They require an actual record type because deconstruction relies on the record's known component accessors; you cannot deconstruct an arbitrary class this way.

code

java · 11 lines
java
record Point(int x, int y) {}

// Type pattern: binds the whole record, still need accessors
if (obj instanceof Point p) {
    int sum = p.x() + p.y();
}

// Record deconstruction pattern: binds the components directly
if (obj instanceof Point(int x, int y)) {
    int sum = x + y;
}

go deeper

for a junior

Can read a record pattern and explain it extracts components into variables instead of calling accessors.

for a middle

Explains the construction/deconstruction symmetry, that one sub-pattern per component is required in order, and that it works in instanceof and switch.

for a senior

Articulates the type-test-plus-bind semantics, that accessors are invoked under the hood, null behavior in instanceof, and the record-type restriction with its rationale.

for a principal

Discusses how record patterns compose with sealed types for exhaustive switches, the design intent (algebraic-data-style data-oriented programming), and migration implications of pattern-based code over accessor calls.

## Background: records and pattern matching A **record** is a compact, immutable Java class declared like `record Point(int x, int y) {}`. From that one line the compiler generates a private final field per component, a public **accessor** named after each component (`x()`, `y()`), a **canonical constructor** taking the components in order, plus `equals`, `hashCode`, and `toString`. The *components* are the named parts listed in the header. **Pattern matching** is a language feature that combines a type test with extracting data. A **type pattern** such as `obj instanceof Point p` does two things at once: it checks `obj instanceof Point` and, if true, declares and assigns a new variable `p` of type `Point`. That variable is called a **pattern variable**. ## What a record deconstruction pattern adds A **record deconstruction pattern** (record pattern) extends the type pattern so it also takes the record apart. Syntax: ```java obj instanceof Point(int x, int y) ``` Read it as: *is `obj` a `Point`? If so, call its accessors and bind `x = obj.x()`, `y = obj.y()`.* The part inside the parentheses — `int x, int y` — is a list of **sub-patterns**, one per record component, in declaration order. Each sub-pattern can itself be a type pattern (`int x`), a `var` pattern (`var x`), or another record pattern (nesting). The pattern's shape deliberately mirrors the canonical constructor. Where you *built* the value with `new Point(3, 4)`, you *take it apart* with `Point(int x, int y)`. This symmetry is why it is called *deconstruction*. ## Why it is more than syntax sugar Without record patterns you write: ```java if (obj instanceof Point p) { int x = p.x(); int y = p.y(); // use x, y } ``` With a record pattern: ```java if (obj instanceof Point(int x, int y)) { // use x, y directly } ``` The code is shorter, the intent ("I care about the parts") is explicit, and the binding count is checked by the compiler against the record's component count. ## Type test semantics For the whole pattern to match, two things must hold: the value must be an instance of the record type, **and** each sub-pattern must match its component. If you write `Point(int x, int y)` the component types must be exactly the declared types (here `int`), so those sub-patterns always match once the outer type matches. If a sub-pattern is itself a type pattern over a *reference* type, that nested type is also tested at runtime. ## Where it works Record patterns are usable in `instanceof` expressions and in `switch` (case labels), introduced as a standard feature in Java 21. They require a **record** type because deconstruction needs the compiler-known component accessors; you cannot deconstruct a normal class with this syntax. ## A null note In `instanceof`, a record pattern does not match `null` (just like a type pattern), so the guarded block is null-safe for the bound value.

  • Why can't you deconstruct an ordinary (non-record) class with this syntax?
    Deconstruction relies on the record's compiler-known components and their accessors, which define the order and types. A general class has no such guaranteed deconstruction contract, so the language restricts record patterns to record types (and the deconstruction-pattern feature for arbitrary classes was not standardized).
  • Must every component appear in the pattern?
    Yes. A record pattern must provide exactly one sub-pattern for every component, in order. You cannot match only some components; use 'var' for the ones you don't care about to keep it terse.

saying these in an interview costs you the question

  • Thinking record patterns work on any class, not just records
  • Believing the components can be listed out of order or partially (every component needs a sub-pattern)
  • Claiming it reads fields directly rather than via the generated accessors
  • Saying instanceof with a record pattern can match null

context