Record Components & Methods
Record Components & Methods
In the previous lesson you saw how to declare a record and why it generates so much boilerplate for you. Now we go one level deeper: how exactly does that generated code work, how can you customise the constructors, and how do you add validation and extra behaviour without losing the compactness that makes records valuable?
What a Record Generates Automatically
Every record component — the variables declared in the header — becomes a private final field. For each component the compiler also generates a public accessor method whose name is exactly the component name (no get prefix). Consider:
You get for free:
public double x()— returns thexfield.public double y()— returns theyfield.- A canonical constructor
Point(double x, double y)that sets both fields. equals(),hashCode(), andtoString()based on the components.
getName(). Record accessors are named name(). If you call point.getX() on a record you will get a compile error — the method is point.x().
The Canonical Constructor
The canonical constructor is the one that takes every component as a parameter in declaration order. By default it is fully generated. You can explicitly declare it to add logic — most commonly validation:
Now every way to create a Range goes through this constructor, so the invariant is always enforced:
The Compact Constructor — Cleaner Validation
Explicitly writing the canonical constructor is verbose because you must repeat the parameter list and assign every field. Java records offer a shorter form called the compact constructor: you omit the parameter list entirely, and the compiler automatically assigns the (possibly modified) parameters to the fields at the end.
Inside the compact constructor you can also reassign the parameter variables before they are stored:
Adding Instance Methods
Records can have any number of regular instance methods. They cannot add mutable state (no extra non-static fields), but they can compute derived values from the components:
Using it feels natural:
Static Members
Records may also have static fields and factory methods, which is the idiomatic way to provide named constructors or constants:
Overriding equals, hashCode, and toString
The generated equals() checks all components with Objects.equals(), and hashCode() uses all components too. This is correct for most records. Override them only when you need custom semantics — for example, case-insensitive equality for a string-backed record:
Summary
Records generate typed accessors (named after the component, no get prefix), a canonical constructor, and value-based equals/hashCode/toString. Use the compact constructor to validate or normalise inputs in a concise way. Add instance methods for derived behaviour and static factory methods for convenient creation. These tools let you build expressive, safe value types with very little code.