What are the main differences between Record and regular class
// java.lang.Record — a special class that cannot be used directly via extends.
🟢 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
- 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
RecordComponentsattribute in the constant pool - JVM knows about canonical components (name, type, annotations)
- Reflection API:
Class.getRecordComponents()returnsRecordComponent[]
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 — fromObject - 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 ofuser.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()notgetName() - ❌ “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]]