Appearance
Lesson 05 · Records
Objectives
After this lesson you will be able to:
- Declare a record and explain what the compiler generates.
- Write canonical and compact constructors and validate components.
- Know the restrictions: implicit
final, no extra instance fields, noextends.
What a record is
A record is a transparent carrier for immutable data. From the component list the compiler generates: a canonical constructor, a private final field and an accessor per component (named x(), not getX()), plus equals, hashCode, and toString.
java
record Point(int x, int y) { }
Point p = new Point(1, 2);
p.x(); // 1 — accessor, NOT getX()
p.equals(new Point(1, 2)); // true — value-based equals
p.toString(); // "Point[x=1, y=2]"Exam trap
A record is implicitly final, its components are private final, and it cannot extends any class (it already extends java.lang.Record). It can implement interfaces. You cannot add extra instance fields — only the components are state (static fields are allowed).
Compact and canonical constructors
The compact constructor omits the parameter list and runs before the implicit field assignments — ideal for validation or normalization. You assign to parameters, not this.x.
java
record Range(int lo, int hi) {
Range { // compact: no (int lo, int hi)
if (lo > hi) throw new IllegalArgumentException("lo > hi");
// fields lo, hi are assigned automatically AFTER this block
}
}You may instead write the canonical constructor in full (then you must assign every field), or add extra constructors that delegate via this(...):
java
record Range(int lo, int hi) {
Range(int hi) { this(0, hi); } // extra constructor delegates to canonical
}Gotcha
In a compact constructor you assign normalized values to the parameter (lo = Math.min(...)), not this.lo. The compiler copies the (possibly reassigned) parameters into the final fields after the block. Writing this.lo = ... there is a compile error.
Records as value types
Because equals/hashCode are component-based, records are perfect map keys and DTOs. They also work directly with record patterns (Lesson 08) for deconstruction.
SDET note
Records make excellent test fixtures and expected-value objects: value equality means assertEquals(expected, actual) "just works" without a hand-written equals. Prefer a record over a class with mutable getters/setters for test data.
Key Takeaways
- A record auto-generates the canonical constructor, accessors (
x()), and value-basedequals/hashCode/toStringfrom its components. - It is implicitly
final, components areprivate final, noextends, no extra instance fields — but it can implement interfaces and have static members. - The compact constructor validates/normalizes by assigning to parameters; fields are set automatically afterward.
- Records are immutable value carriers — ideal for keys, DTOs, and record-pattern deconstruction.
Lesson Quiz
How do you read component x of record Point(int x, int y)?
Which is ILLEGAL for a record?
In a compact constructor, how do you normalize lo?
record Range(int lo, int hi) { Range { /* here */ } }What does new Point(1,2).equals(new Point(1,2)) return?
Can a record declare an extra instance field beyond its components?
Next: Sealed Types. Run the matching code in labs/src/main/java/com/jse21/m03_oop/.