What is the canonical constructor of a Java record, and what does the compiler generate if you don't write one?
answer
- Parameters match components one-for-one
- Auto-generated if you don't write it
- Plain field assignment by default
- Visibility can't be narrower than the record
- Only entry point for component data
basics
~20 sThe canonical constructor is a record's main constructor whose parameters match its components, one-for-one, by name and type. If you don't write one, the compiler generates it for you, and it just assigns each parameter to the matching field.
solid answer
~40 sEvery record has a canonical constructor: a constructor whose parameter list exactly matches the record's component list in order, type, and (by convention) name. When you declare `record Point(int x, int y) {}`, the compiler auto-generates a canonical constructor that simply assigns each parameter to the corresponding final field (`this.x = x; this.y = y;`). You only write the canonical constructor yourself when you need to do something extra such as validation, normalization, or defensive copying. The auto-generated one is public if the record is public, and you cannot reduce its visibility below the record's own. Records also generate the component accessors, `equals`, `hashCode`, and `toString` from the same components, so the canonical constructor is the one place where all component values enter the object.
code
java · 12 lines// No constructor written -> compiler generates the canonical one:
record Point(int x, int y) {}
Point p = new Point(3, 4); // runs the auto-generated canonical constructor
System.out.println(p.x()); // 3 (accessor is x(), not getX())
// Writing it yourself to add validation:
record Range(int lo, int hi) {
Range { // compact form (assignment is implicit)
if (lo > hi) throw new IllegalArgumentException("lo > hi");
}
}go deeper
Can state that a record auto-generates a constructor matching its components and that it assigns the fields.
Explains when to hand-write the canonical constructor (validation/normalization) and that it is the only data entry point; knows the visibility rule.
Connects the canonical constructor to derived equals/hashCode/toString and immutability invariants, and discusses defensive copying.
Frames the canonical constructor as the invariant-enforcement boundary for value types and weighs records vs classes / builders for API design.
## What a record is A **record** (Java 16+) is a special kind of class for carrying immutable data. You declare it by listing its **components** in parentheses: ```java record Point(int x, int y) {} ``` Each **component** (here `x` and `y`) is a named, typed piece of data the record holds. From this single line the compiler generates several things automatically: a `private final` field per component, an **accessor** method per component (named exactly `x()` and `y()`, not `getX()`), and the `equals`, `hashCode`, and `toString` methods derived from all components. ## The canonical constructor A **constructor** is the special method that runs when you create an object with `new`. The **canonical constructor** of a record is the constructor whose parameter list matches the record's component list **exactly** — same number, same types, in the same order. For `Point(int x, int y)`, the canonical constructor has the signature `Point(int x, int y)`. If you do not write any constructor, the compiler **auto-generates** the canonical constructor. Its body simply assigns each parameter to the matching field: ```java // conceptually generated for you: public Point(int x, int y) { this.x = x; this.y = y; } ``` This is why a bare `record Point(int x, int y) {}` is fully usable — the constructor exists even though you never typed it. ## When you write it yourself You declare the canonical constructor explicitly only when you need extra behavior — typically **validation** (reject bad input), **normalization** (clean up input, e.g. trim a string), or **defensive copying** (copy a mutable argument so the record stays immutable). When you write a *full* canonical constructor you must assign every field yourself; if you forget one, the compiler reports a definite-assignment error. ## Visibility rule The canonical constructor's access level **cannot be more restrictive than the record itself**. A `public record` must have a `public` canonical constructor. You may *widen* but not narrow it. ## Why it matters The canonical constructor is the **single entry point** for all component data into the object. Because the auto-generated `equals`/`hashCode`/`toString` are derived from the component fields, whatever you do in the canonical constructor (validation, normalization) governs the invariants of the whole record.
- Can you make the canonical constructor private on a public record?No. The canonical constructor's access cannot be more restrictive than the record's. A public record requires a public (or at least equally accessible) canonical constructor; you may widen but not narrow it.
- What does the auto-generated canonical constructor's body do?It assigns each parameter to its matching final field, e.g. this.x = x; this.y = y; for every component, with no validation or copying.
Think of a record as a form with labeled fields. The canonical constructor is the only pen allowed to fill in the form: by default it copies each answer straight into its box, but you can swap in a smarter pen that checks or cleans each answer first.
saying these in an interview costs you the question
- Saying records have no constructor — every record always has a canonical constructor, generated if not written.
- Calling the accessors getX()/getY() — record accessors are named exactly after the component, e.g. x().
- Claiming you can make the canonical constructor private on a public record.