Cosmic Module

J

Qubits of DPK

March 14, 2026

Core Java

Layman Explanation

Every time you create a data-holding class in Java (like a DTO), you write the same boilerplate: private fields, constructor, getters, equals(), hashCode(), toString(). That's 30+ lines of code. Records do ALL of that automatically in ONE line.

Real World Analogy

Filling a physical form vs a digital auto-fill form. Physical: write name, address, phone 5 times on different sections. Digital: fill once, auto-fills everywhere. Records are Java's auto-fill for data classes.

The Problem Records Solve

java
QUBITS OF DPK
1// Traditional data class (DTO) — 30+ lines of boilerplate!
2class Point {
3    private final int x;
4    private final int y;
5
6    public Point(int x, int y) {
7        this.x = x;
8        this.y = y;
9    }
10
11    public int x() { return x; }
12    public int y() { return y; }
13
14    @Override
15    public boolean equals(Object o) {
16        if (this == o) return true;
17        if (!(o instanceof Point)) return false;
18        Point p = (Point) o;
19        return x == p.x && y == p.y;
20    }
21
22    @Override
23    public int hashCode() {
24        return Objects.hash(x, y);
25    }
26
27    @Override
28    public String toString() {
29        return "Point[x=" + x + ", y=" + y + "]";
30    }
31}

Record — Same in ONE Line!

java
QUBITS OF DPK
1// Record — all boilerplate auto-generated!
2record Point(int x, int y) { }
3
4// Usage
5Point p1 = new Point(3, 5);
6Point p2 = new Point(3, 5);
7Point p3 = new Point(1, 2);
8
9System.out.println(p1.x());         // 3 (auto-generated getter)
10System.out.println(p1.y());         // 5
11System.out.println(p1);             // Point[x=3, y=5] (auto toString)
12System.out.println(p1.equals(p2));  // true (auto equals)
13System.out.println(p1.equals(p3));  // false
14System.out.println(p1.hashCode()); // consistent with equals

What Java Auto-Generates

javascript
QUBITS OF DPK
1private final fields for each component
2public canonical constructor
3public accessor methods (x(), y())NOT getX(), getY()!
4equals() based on all components
5hashCode() based on all components
6toString() like Point[x=3, y=5]

Records Are Immutable

java
QUBITS OF DPK
1record Person(String name, int age) { }
2
3Person p = new Person("Deepak", 25);
4p.name = "Arjun";  // ❌ compile error! Fields are final
Records are implicitly final and their fields are private final.
Perfect for immutable data transfer objects.

Custom Compact Constructor

java
QUBITS OF DPK
1record Person(String name, int age) {
2
3    // Compact constructor — no parameters needed, auto-assigns
4    Person {
5        if (name == null || name.isBlank()) {
6            throw new IllegalArgumentException("Name cannot be blank");
7        }
8        if (age < 0 || age > 150) {
9            throw new IllegalArgumentException("Invalid age: " + age);
10        }
11        // No need to write this.name = name; — auto-done!
12    }
13}
14
15Person p1 = new Person("Deepak", 25);  // ✅
16Person p2 = new Person("", 25);         // ❌ throws exception
17Person p3 = new Person("Arjun", -1);   // ❌ throws exception

Adding Methods to Records

java
QUBITS OF DPK
1record Circle(double radius) {
2
3    // Custom method
4    double area() {
5        return Math.PI * radius * radius;
6    }
7
8    double circumference() {
9        return 2 * Math.PI * radius;
10    }
11
12    // Static methods allowed
13    static Circle unit() {
14        return new Circle(1.0);
15    }
16}
17
18Circle c = new Circle(5.0);
19System.out.println(c.area());          // 78.53...
20System.out.println(c.radius());        // 5.0 (auto accessor)
21System.out.println(Circle.unit());     // Circle[radius=1.0]

Records with Sealed Classes (Power Combo!)

java
QUBITS OF DPK
1sealed interface Expr permits Num, Add, Multiply { }
2
3record Num(int value)              implements Expr { }
4record Add(Expr left, Expr right)  implements Expr { }
5record Multiply(Expr left, Expr right) implements Expr { }
6
7// Evaluate with pattern matching
8int eval(Expr expr) {
9    return switch (expr) {
10        case Num(int v)        -> v;
11        case Add(var l, var r) -> eval(l) + eval(r);
12        case Multiply(var l, var r) -> eval(l) * eval(r);
13    };
14}

Production Use Cases

java
QUBITS OF DPK
1// 1. DTOs (Data Transfer Objects)
2record UserDTO(long id, String name, String email) { }
3record ProductDTO(String sku, String name, double price) { }
4
5// 2. API Responses
6record ApiResponse<T>(int status, String message, T data) { }
7
8// 3. Configuration
9record DatabaseConfig(String url, String username, int maxPoolSize) { }
10
11// 4. Event objects
12record PaymentEvent(String transactionId, double amount, String status) { }
13
14// 5. Value objects (DDD)
15record Money(BigDecimal amount, String currency) {
16    Money {
17        Objects.requireNonNull(amount);
18        Objects.requireNonNull(currency);
19        if (amount.compareTo(BigDecimal.ZERO) < 0) {
20            throw new IllegalArgumentException("Amount cannot be negative");
21        }
22    }
23}

️ All Traps

java
QUBITS OF DPK
1// Trap 1 — Accessors are NOT getX(), they are x()
2record Point(int x, int y) { }
3Point p = new Point(3, 5);
4p.getX();  // ❌ doesn't exist!
5p.x();     // ✅ correct accessor
6
7// Trap 2 — Records are implicitly final
8class MyPoint extends Point { }  // ❌ can't extend records!
9
10// Trap 3 — Records can implement interfaces
11record Point(int x, int y) implements Comparable<Point> {
12    @Override
13    public int compareTo(Point other) {
14        return Integer.compare(this.x, other.x);
15    }
16}
17
18// Trap 4 — Cannot add mutable state
19record Person(String name) {
20    int age = 25;  // ❌ instance fields not allowed!
21    static int count = 0;  // ✅ static fields OK
22}
23
24// Trap 5 — Cannot extend another class
25record Foo(int x) extends Bar { }  // ❌ records implicitly extend Record

30-Second Interview Answer

"Records introduced in Java 16 (stable in 17) are immutable data classes that automatically generate private final fields, canonical constructor, accessors (named after field, not getX style), equals, hashCode, and toString. They eliminate boilerplate for DTOs and value objects. Records are implicitly final and cannot extend other classes but can implement interfaces. Custom validation goes in the compact constructor. Key trap: accessors are x() not getX(). Used heavily for DTOs, API responses, events, and value objects in DDD."

Interview Questions & MAANG-Level Answers

Q1. What is a Record in Java and what does it auto-generate?
Record (Java 16+, stable Java 17) is a concise data class for immutable data carriers. record Point(int x, int y) {} auto-generates: (1) private final fields x and y. (2) Public canonical constructor Point(int x, int y). (3) Public accessor methods x() and y() (NOT getX()!). (4) equals() based on all components. (5) hashCode() based on all components. (6) toString() like Point[x=3, y=5]. Records are implicitly final and extend java.lang.Record.
Q2. How are record accessors named differently from regular getters?
Record accessors use the field name directly WITHOUT the "get" prefix: record Person(String name, int age) — accessors are person.name() and person.age(), NOT person.getName(). This is a common interview trap and a frequent source of bugs when migrating from POJOs to records. Frameworks like Jackson handle this automatically with @JsonProperty or by configuring the object mapper to recognize record-style accessors.
Q3. Can a record extend another class?
No. Records implicitly extend java.lang.Record and Java doesn't support multiple class inheritance. record Foo(int x) extends Bar {} — compile error. However, records CAN implement interfaces:
java
QUBITS OF DPK
1record Point(int x, int y) implements Comparable<Point> {
2    public int compareTo(Point other) {
3        return Integer.compare(this.x, other.x);
4    }
5}
This makes records work well with sealed interfaces for algebraic data type modeling.
Q4. What is a compact constructor in a record?
A compact constructor is a special record constructor that automatically receives all component values but doesn't need to explicitly assign them — Java does it automatically after the body runs:
java
QUBITS OF DPK
1record Person(String name, int age) {
2    Person {  // compact: no params listed
3        Objects.requireNonNull(name, "name required");
4        if (age < 0) throw new IllegalArgumentException("Invalid age");
5        name = name.trim();  // can normalize components before auto-assignment
6        // this.name = name; auto-inserted by compiler
7    }
8}
Perfect for validation and normalization without boilerplate.
Q5. What is the difference between a record and a regular immutable class?
A regular immutable class requires manually writing: private final fields, constructor, getters, equals, hashCode, toString — typically 30-50 lines. A record does all of this in ONE line. Behavioral difference: records are transparent (all components exposed), while regular classes can have private state not in the constructor. Records are for pure data carriers; regular immutable classes for richer value objects with complex invariants or private implementation details.
Q6. Can records implement interfaces?
Yes — this is the primary extension mechanism for records:
java
QUBITS OF DPK
1sealed interface Expr permits Num, Add {}
2record Num(int value) implements Expr {}
3record Add(Expr left, Expr right) implements Expr {}
This combination of sealed interfaces + records is the Java way of expressing algebraic data types (like Haskell/Scala), enabling exhaustive pattern matching.
Q7. When would you use a record vs a regular class?
Use record when: pure data carrier (DTO, value object, API response), all fields should be public and final, simple equality based on all fields, no complex behavior or mutable state needed. Example: record UserDTO(long id, String name, String email). Use regular class when: need mutable state, need to hide some fields from equals/hashCode, need to extend another class, complex behavior beyond data access, or need different access patterns.