skip to content

Record Declaration & Generated Members

Record components become private final fields with same-named accessors, and the compiler derives equals, hashCode and toString from them. The usual first question is how much boilerplate this actually replaces and what the accessor is called.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Java record, and what does the compiler generate for you from its components?

level: juniorimportance: must knowfreq 78%

answer

  1. record Header(components) {}
  2. private final field + same-name accessor per component
  3. auto equals / hashCode / toString from all components
  4. accessor is x(), not getX()
  5. implicitly final, extends java.lang.Record

basics

~20 s

A record is a compact class for holding immutable data. You list its components in the header, and the compiler auto-generates a private final field plus an accessor for each, a constructor, and equals, hashCode, and toString.

solid answer

~40 s

A record is a special class declared as `record Point(int x, int y) {}`. The names in parentheses are its components. For each component the compiler creates a `private final` field and a public accessor method named exactly after the component (e.g. `x()`, not `getX()`). It also generates a canonical constructor that takes all components, plus value-based `equals`, `hashCode`, and `toString` derived from all components. The result is an immutable data carrier with almost no boilerplate. Records are implicitly `final`, extend `java.lang.Record`, and cannot extend another class. Introduced as a preview in Java 14 and finalized in Java 16, records are ideal for DTOs, map keys, and return types that bundle several values.

code

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

// Usage
Point p = new Point(1, 2);
int a = p.x();          // accessor named after the component (not getX)
int b = p.y();
System.out.println(p);  // Point[x=1, y=2]  -> generated toString

Point q = new Point(1, 2);
System.out.println(p.equals(q)); // true  -> value-based equals
System.out.println(p.hashCode() == q.hashCode()); // true

go deeper

for a junior

Can declare a record, list its components, and use the generated accessor, equals, and toString. Knows accessors are x() not getX().

for a middle

Explains exactly what the compiler generates (field, accessor, canonical constructor, equals/hashCode/toString) and that records are immutable and implicitly final.

for a senior

Articulates the design intent (transparent immutable data carrier), the value-based equality contract derived from all components, the java.lang.Record superclass constraint, and good use cases (DTOs, keys, return types) vs. where a normal class is better.

for a principal

Frames records as nominal product types / value-based aggregates, discusses how value-based equality enables safe use as keys and in pattern-matching deconstruction, and weighs API/compatibility implications of exposing records across module or library boundaries.

## What problem records solve Before records, a simple immutable data class in Java required a lot of repetitive ("boilerplate") code: a private field per value, a constructor, a getter per field, and hand-written `equals`, `hashCode`, and `toString`. This was tedious and error-prone. A **record** is a concise way to declare a class whose main purpose is to **carry data**. ## Anatomy of a declaration ```java public record Point(int x, int y) { } ``` - The keyword `record` replaces `class`. - The list in parentheses `(int x, int y)` is the **record header**, and each entry (`x`, `y`) is a **component**. A component is just a named, typed piece of data the record holds. - The body `{ }` can be empty — everything essential is generated. ## What the compiler generates From the components, the compiler automatically produces: 1. **A `private final` field per component.** `final` means it is assigned once and never changes — this is what makes records immutable by default. (Immutable = its state cannot be modified after construction.) 2. **A public accessor method per component**, named *exactly* after the component — `x()` and `y()`, **not** `getX()`/`getY()`. Java records deliberately drop the JavaBeans `get` prefix. 3. **A canonical constructor** taking all components in declared order, which assigns each argument to the matching field. 4. **`equals(Object)`** — two records are equal when they are the same record type and *all* their components are equal (value-based equality, not reference identity). 5. **`hashCode()`** — derived from all the components, consistent with `equals`. 6. **`toString()`** — a readable form like `Point[x=1, y=2]`. ## Built-in constraints - A record is **implicitly `final`** — you cannot subclass it. - It **implicitly extends `java.lang.Record`** — so it cannot extend any other class (Java has single inheritance of implementation), but it *can* implement interfaces. - You may not add extra **instance** fields beyond the components (static fields are allowed). ## When to use one Records shine for **DTOs** (data transfer objects), **value objects**, multi-value return types, and **map/set keys** (because correct `equals`/`hashCode` come for free). Avoid them when you need mutable state or an inheritance hierarchy. ## History Records were a preview feature in Java 14 (2020) and became a permanent language feature in Java 16 (2021).

  • Why are record accessors named x() instead of getX()?
    Records intentionally break with the JavaBeans get/set convention. The accessor name matches the component name exactly, signaling that a record is a transparent data carrier rather than a mutable bean. The language designers chose `x()` to read as 'the x of this record' and to allow component names to drive the API directly.
  • Can a record have zero components?
    Yes. `record Empty() {}` is legal — it produces a class with no fields, a no-arg canonical constructor, and equals/hashCode/toString based on no components (so all instances are equal). It is occasionally used as a sealed-hierarchy marker.

Think of a record as a pre-printed shipping label: you fill in the fixed boxes (the components) once, the label is sealed (immutable), and anyone can read the boxes back — but nobody re-prints over them.

saying these in an interview costs you the question

  • Calling the accessor getX() — records use x() with no get prefix.
  • Saying records are mutable — components are private final, so a record's own fields cannot be reassigned.
  • Claiming the compiler generates setters — records have no setters by design.
  • Saying a record can extend another class — it implicitly extends java.lang.Record and Java forbids multiple class inheritance.
  • Confusing a record with a regular class that simply has fewer lines — the generated equals/hashCode/toString and the final-field guarantees are part of the language contract.

context

open as a page

How does a record's generated equals, hashCode, and toString behave, and what is the resulting equality contract?

level: middleimportance: must knowfreq 70%

basics

~20 s

Two records are equal if they are the same record type and every component is equal. hashCode is computed from all components and stays consistent with equals. toString prints the type name and each component, like Point[x=1, y=2].

open as a page

What is the canonical constructor of a record, and how do the compact and explicit canonical forms differ?

level: juniorimportance: should knowfreq 52%

basics

~20 s

The canonical constructor takes exactly the record's components, in order, and assigns each to its field. The compact form omits the parameter list and lets you validate or normalize the arguments; the fields are assigned automatically at the end. An explicit canonical constructor writes the parameter list and assignments yourself.

open as a page

When would you choose a record over a regular class, and what does a record forbid that a normal class allows?

level: middleimportance: should knowfreq 60%

basics

~20 s

Use a record when you mainly need an immutable bundle of data with sensible equals, hashCode, and toString — DTOs, value objects, map keys, return types. A record can't extend another class, can't be subclassed, and can't add extra instance fields.

open as a page

A record's components are private final fields — does that make a record fully immutable? Explain the limits.

level: seniorimportance: should knowfreq 55%

basics

~20 s

The record's own fields can't be reassigned, so the record is shallowly immutable. But if a component is a reference to a mutable object (like a List or array), callers can still change that object's contents through the shared reference.

open as a page