🪆 Enums, Records & Nested Types · Intermediate

Record limitations in Java

Implicitly final, fields final, cannot extend classes, shallowly immutable.

🧩 The mysteryRecords are final, their fields are final… so a record can never change, right? One line of code says otherwise.

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.

🔮 Predict it

Sneaking in a field

What happens?

record Point(int x, int y) {
    String label = "pt";
}
void main() {
    System.out.println(new Point(1, 2));
}
  1. Point[x=1, y=2]
  2. Point[x=1, y=2, label=pt]
  3. 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);
    }
}
⚠️ The trap

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 { }   // error
🔮 Predict it

Shallow 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());
}
  1. [Zoe]
  2. []
  3. 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.

🤔 Think first

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.

💼 In the real world

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

  1. Implicitly final — no subclasses
  2. Can implement interfaces, but can't extend classes
  3. No extra instance fields; static fields are fine
  4. Shallowly immutable: mutable components stay mutable
🤯 Did you know?

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));
}
  1. Point[x=1, y=2]
  2. Point[x=1, y=2, z=0]
  3. 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 { }
  1. No: a record can't extend another type
  2. Yes, it compiles fine
  3. Yes, but Dog can't call name()
  4. 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.

Next: classes inside classes. A static nested class is like a roommate — no strings attached to the outer object.