Introducing Records
Introducing Records
Every Java developer has written a class whose sole purpose is to hold a handful of values and pass them around — a coordinate pair, an API response, a search result. Writing that class the traditional way means keyboard-filling a constructor, a field for every component, getters, equals(), hashCode(), and toString(). That is roughly 30–50 lines for what is conceptually just a named tuple. Java 16 made records a permanent language feature to solve this problem exactly.
The problem records solve
Consider a simple class that represents a 2-D point:
Every time the design changes you touch multiple methods. The intent — "a point has an x and a y" — is buried under the implementation noise.
The record keyword
A record declaration names the class and its components in one line:
That single line gives you everything the 30-line version gave you:
- Two
private finalfields,xandy. - A canonical constructor —
Point(int x, int y)— that assigns both fields. - Accessor methods
x()andy()(note: nogetprefix — that is intentional). - A value-based
equals()that compares all components. - A consistent
hashCode()derived from all components. - A readable
toString()that printsPoint[x=3, y=7].
Using a record
Notice that equals is value-based: two separate Point objects with the same coordinates are equal, just as you would expect from a data class.
Records are implicitly final
A record class is final by default — you cannot extend it. You can implement interfaces, but you cannot create a subclass of a record. This is not a restriction; it is a deliberate design choice that keeps the meaning of a record clear: its identity is entirely defined by its components, and allowing subclasses to add hidden state would break that guarantee.
Records are immutable by design
Every component field is private final. There are no setters generated. Once a record is constructed, its state cannot change. This immutability is one of the most important properties records bring — it makes them safe to share across threads and easy to reason about.
equals() and hashCode(), they work correctly as Map keys, in Sets, and in collections — no defensive copying needed.
A second example: an HTTP response summary
What records are NOT
- They are not JavaBeans — no
getX()style accessors, no no-arg constructor, no setters. - They are not just a shorthand for any class — use records only when the class truly is its data (a result, a command, a coordinate, a range). If you need mutability or inheritance, use a regular class.
- They are not value types (like C structs) — they are still heap-allocated reference objects in the current JVM.
Summary
Records are Java's answer to the boilerplate tax on immutable data classes. The record keyword replaces dozens of lines of mechanical code with a single declaration that clearly expresses what the type is. The compiler generates the canonical constructor, accessors, equals, hashCode, and toString for you — and the resulting type is final, immutable, and correct by construction. In the next lesson you will see how to add your own methods to a record and customise the generated constructor when you need to validate input.