Question 2 · Section 20

What are the main differences between Record and regular class

// java.lang.Record — a special class that cannot be used directly via extends.

Language versions: English Russian Ukrainian

🟢 Junior Level

Record is a special type of class in Java designed for storing data. The key difference: Records are created to be simple data containers, while regular classes are universal tools.

Main differences:

| Record | Regular class | | ———————————– | ———————– | | All fields private final | Any fields | | Cannot change fields after creation | Can change fields | | Inherits from java.lang.Record | Inherits from Object | // java.lang.Record — a special class that cannot be used directly via extends. | implicit final — cannot be extended | Can inherit from | | Auto-generation of equals, hashCode, toString | Must write manually or via IDE |

Example:

// Record — one line
public record Point(int x, int y) {}

// Regular class — 30+ lines
public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() { return x; }
    public int y() { return y; }

    // plus equals, hashCode, toString...
}

🟡 Middle Level

How it works

Record is a restricted form of class where the compiler takes care of boilerplate:

1. Constructor:

// Record — canonical constructor is auto-generated
public record User(String name, int age) {}

// Can add your own constructor (must call canonical)
public record User(String name, int age) {
    public User(String name) {
        this(name, 0);  // delegation
    }
}

2. Getters:

// Record — without get prefix!
User user = new User("John", 25);
user.name();  // ✅ NOT user.getName()
user.age();   // ✅ NOT user.getAge()

3. Inheritance:

// ❌ Record cannot be extended
public class Bad extends Point {}  // compilation error

// ❌ Record cannot extend anything (except java.lang.Record)
public record Bad extends Object {}  // compilation error

// ✅ Record can implement interfaces
public record User(String name) implements Serializable {}

Practical Application

When to use Record:

  • DTO for REST API
  • Keys in HashMap/HashSet
  • Returning multiple values from a method
  • Value objects in DDD

When to use regular class:

  • Need mutable state
  • Need additional methods/fields
  • Need inheritance
  • Need builder pattern with mutable fields

Common Mistakes

  1. Trying to make mutable fields: ```java public record BadRecord() { // ❌ Non-static field — compilation error public int mutableField = 0; }

// ✅ Only static public record GoodRecord() { public static int counter = 0; }


2. **Expecting get/set methods:**
```java
public record User(String name) {}
User u = new User("John");

// ❌ u.getName() — no such method!
// ✅ u.name() — correct call

🔴 Senior Level

Internal Implementation

Class file structure:

// Record — this is a final class with ACC_RECORD flag
ClassFile {
    access_flags: ACC_FINAL | ACC_RECORD
    super_class: java/lang/Record
    fields: all private final
    methods: canonical constructor + accessors + equals/hashCode/toString
}

Differences at JVM level:

  • Record has a special RecordComponents attribute in the constant pool
  • JVM knows about canonical components (name, type, annotations)
  • Reflection API: Class.getRecordComponents() returns RecordComponent[]

Architectural Trade-offs

Record:

  • ✅ Minimal boilerplate
  • ✅ Guaranteed immutability
  • ✅ Optimized for value types (Project Valhalla)
  • ❌ No flexibility
  • ❌ Cannot use with JPA entities (needs no-arg constructor)

Regular class:

  • ✅ Full control
  • ✅ JPA compatibility
  • ✅ Builder, mutable state
  • ❌ More code
  • ❌ Manual maintenance of equals/hashCode

Edge Cases

1. Compact constructor for validation:

public record Email(String value) {
    public Email {
        if (!value.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
}

2. Annotations on components:

public record User(
    @JsonProperty("user_name") String name,
    @Min(18) int age
) {}
// Annotation is applied to field, constructor parameter AND accessor

3. Serialization:

// Record uses a special deserialization mechanism
// via canonical constructor (JEP 445, Java 21)
// instead of readObject/writeObject

Performance

Operation          | Record | Regular class (final fields)
-------------------|--------|---------------------------
Object creation    | 8 ns   | 8 ns
equals()           | 15 ns  | 15 ns (manual implementation)
hashCode()         | 12 ns  | 12 ns

Difference is minimal — Record is not inferior
// Approximate values. Depend on JVM/hardware.

Production Experience

// DTO in Spring Boot 3.x — Records became the standard
public record CreateUserRequest(
    @NotBlank String name,
    @Email String email,
    @Min(18) int age
) {}

// JPA entities — cannot use Records yet
// (need mutable state, no-arg constructor, lazy loading proxies)
@Entity
public class User {  // regular class
    @Id private Long id;
    private String name;
}

Best Practices

// ✅ Record for immutable DTO
public record OrderDto(String id, Instant createdAt, BigDecimal amount) {}

// ✅ Record for value objects
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        Objects.requireNonNull(amount);
        Objects.requireNonNull(currency);
    }
}

// ❌ Record for JPA entity
// ❌ Record for mutable collections without protection
public record BadRecord(int[] values) {}  // array is mutable!

🎯 Interview Cheat Sheet

Must know:

  • Record is a restricted kind of class: only final fields, cannot inherit, cannot extend
  • Record inherits from java.lang.Record, regular class — from Object
  • Auto-generation of equals, hashCode, toString — Record vs manual code in regular class
  • Record is immutable by design; regular class can be mutable or immutable
  • Record cannot be used with JPA (needs no-arg constructor, mutable state)
  • Record getters without get: user.name() instead of user.getName()

Common follow-up questions:

  • When to choose Record vs regular class? — Record for immutable DTO/value objects, regular class for JPA, mutable state, builder
  • Can you add extra methods to a Record? — Yes, any static and instance methods, but not instance fields
  • Is Record faster than a regular class? — No, performance is identical, difference < 5%
  • Can you override equals/hashCode in Record? — Yes, but it’s rarely needed

Red flags (DO NOT say):

  • ❌ “Record is a subclass of regular class” — Record inherits from java.lang.Record, a special class
  • ❌ “Record supports inheritance” — Record is implicit final
  • ❌ “Record uses get/set” — accessors without get: name() not getName()
  • ❌ “Record is suitable for JPA” — JPA requires no-arg constructor and mutable fields

Related topics:

  • [[1. What is Record in Java and since which version are they available]]
  • [[3. Can you inherit from Record or have Record inherit from another class]]
  • [[5. What methods are automatically generated for a Record]]
  • [[9. Are Record fields final]]