🪆 Enums, Records & Nested Types · Intermediate

Records in Java

Transparent data carriers: auto constructor, accessors, equals, hashCode, toString.

🧩 The mysteryA simple Point class with x and y used to take ~40 lines: constructor, getters, equals, hashCode, toString. Java 16 squeezed it into one line. What's the catch?

One line, whole class

A record (Java 16) is a transparent carrier for data. From the header alone you get: private final fields, a canonical constructor, accessors **x() and y(), plus equals, hashCode and toString** based on all components.

record Point(int x, int y) { }
 
var p = new Point(1, 2);
p.x();   // 1
🔮 Predict it

Printing a record

What does this print?

record Pair(String a, int b) { }
void main() {
    System.out.println(new Pair("hi", 7));
}
  1. Pair[a=hi, b=7]
  2. Pair(a=hi, b=7)
  3. {a=hi, b=7}
Show the answer

The generated toString prints the **record name, then name=value pairs in square brackets**.

🤔 Think first

Twins

var a = new Point(3, 4); var b = new Point(3, 4); — what are a == b and a.equals(b)?

Think about it, then reveal the answer

a == b is false: two new calls make two objects. a.equals(b) is true: records generate a value-based equals that compares every component (with a matching hashCode).

⚠️ The trap

getX()? Nope

Record accessors use the component name exactly: p.x(), not p.getX(). Calling getX() is a compile error — no such method exists.

record Point(int x, int y) { }
p.x();      // ok
p.getX();   // compile error

Records can do more

Records can have extra methods, static fields and static methods, and can implement interfaces. For new Rect(3, 5), r.w() + r.area() is 3 + 15 = 18.

record Rect(int w, int h) {
    int area() { return w * h; }
}
var r = new Rect(3, 5);
r.w() + r.area();   // 18
💼 In the real world

Where records shine

API request and response objects, results of database queries, composite map keys, and returning two values from a method. Because equals and hashCode come for free, records are safe as HashMap keys — no forgotten field in a hand-written equals.

Key takeaways

  1. Accessors are named x(), not getX()
  2. equals/hashCode compare every component (value-based)
  3. toString looks like Point[x=1, y=2]
  4. You can add methods and static fields, and implement interfaces

💡 A labelled envelope: what's inside is exactly what's written on the front.

🤯 Did you know?

Records were previewed in Java 14 and 15 before becoming final in Java 16 — two rounds of real-world feedback before the design was locked in.

Practice questions

What does this print?

record Point(int x, int y) { }
void main() {
    var p = new Point(1, 2);
    System.out.println(p);
}
  1. Point[x=1, y=2]
  2. Point(x=1, y=2)
  3. {x=1, y=2}
  4. Point[1, 2]
Check your answer

Point[x=1, y=2]. The generated toString prints the record name followed by name=value pairs in square brackets.

What does this print?

record Point(int x, int y) { }
void main() {
    var a = new Point(3, 4);
    var b = new Point(3, 4);
    System.out.println((a == b) + " " + a.equals(b));
}
  1. false true
  2. true true
  3. false false
  4. true false
Check your answer

false true. Two `new` calls create two objects, so == is false. The generated equals compares components, so equals is true.

Next: how do you stop someone from creating new Range(5, 1), where low is bigger than high? Records have a special "compact" constructor.