Record limitations in Java
Implicitly final, fields final, cannot extend classes, shallowly immutable.
Deliberately strict
A record is implicitly final (no subclasses) and **implicitly extends java.lang.Record, so it can't extend another class. Its fields are private final, and you can't add extra instance fields** — the state is exactly its components.
Sneaking in a field
What happens?
record Point(int x, int y) {
String label = "pt";
}
void main() {
System.out.println(new Point(1, 2));
}Point[x=1, y=2]Point[x=1, y=2, label=pt]Compile error
Show the answer
A record can't declare instance fields beyond its components. (A static field would be fine — it belongs to the type, not the instance.)
What IS allowed
Static fields and methods, extra instance methods, and implementing interfaces. What's illegal: extra instance fields, an extends clause, and assigning a component field (like this.x = 5) — the fields are final.
record Point(int x, int y)
implements Comparable<Point> {
static int created = 0;
public int compareTo(Point o) {
return Integer.compare(x, o.x);
}
}No family trees
extends is not allowed in a record declaration at all — a record already extends java.lang.Record and is final.
record Animal(String name) { }
record Dog(String name)
extends Animal { } // errorShallow immutability
The record's field is final. What prints?
record Team(List<String> names) { }
void main() {
var list = new ArrayList<String>();
var team = new Team(list);
list.add("Zoe");
System.out.println(team.names());
}[Zoe][]Throws UnsupportedOperationException
Show the answer
The field is final — it will always point at the same list. But that list is mutable, so changing it changes what the record shows. Records are only shallowly immutable.
Make it deep
How would you make Team truly unchangeable?
Think about it, then reveal the answer
Add a compact constructor that stores a copy: Team { names = List.copyOf(names); }. Now the record holds its own unmodifiable list, cut off from the caller's.
Records vs entities
Records make great DTOs and query results, but not JPA/Hibernate entities: those need a no-arg constructor, mutable fields and non-final classes the framework can proxy. Know the limits, and use each tool where it fits.
Key takeaways
- Implicitly final — no subclasses
- Can implement interfaces, but can't extend classes
- No extra instance fields; static fields are fine
- Shallowly immutable: mutable components stay mutable
You can't write class X extends Record yourself — javac says "classes cannot directly extend Record". Only real record declarations get that parent.
Practice questions
What does this print?
record Point(int x, int y) {
int z = 0;
}
void main() {
System.out.println(new Point(1, 2));
}- Point[x=1, y=2]
- Point[x=1, y=2, z=0]
- Compile error
Check your answer
Compile error. A record's state is exactly its components. Declaring another instance field is a compile error (a static field would be fine).
Does this compile?
record Animal(String name) { }
record Dog(String name) extends Animal { }- No: a record can't extend another type
- Yes, it compiles fine
- Yes, but Dog can't call name()
- Only if Animal is declared abstract
Check your answer
No: a record can't extend another type. Records implicitly extend java.lang.Record and are final, so `extends` is not allowed in a record declaration at all.