Can you inherit from Record or have Record inherit from another class
Records are created for simple data transfer, not for object-oriented inheritance.
🟢 Junior Level
No, you cannot. Records in Java have two strict restrictions:
- Record cannot be extended — Record is
implicit final - Record cannot extend another class — all Records automatically inherit from
java.lang.Record
// ❌ Cannot inherit from Record
public record Point(int x, int y) {}
public class Bad extends Point {} // compilation error
// ❌ Record cannot extend another class
public record Bad extends Object {} // compilation error
// Record always extends java.lang.Record
But you can implement interfaces:
public record User(String name) implements Serializable, Comparable<User> {
@Override
public int compareTo(User other) {
return this.name.compareTo(other.name);
}
}
🟡 Middle Level
Why these restrictions?
Records are created for simple data transfer, not for object-oriented inheritance.
Reasons:
- Immutability guarantee — if Record could be extended, a subclass could add mutable fields
- Predictable behavior —
equals(),hashCode()always work the same way - JVM optimization — JIT can make assumptions about structure
- Value types compatibility — preparation for Project Valhalla
Practical Application
Instead of inheritance — use composition:
// ❌ Cannot do
public record AuditedRecord(String name) extends BaseEntity {}
// ✅ Can do via composition
public record BaseEntity(LocalDateTime createdAt, String createdBy) {}
public record User(String name, BaseEntity audit) {}
// Or via interface
public interface Auditable {
LocalDateTime createdAt();
String createdBy();
}
public record User(String name, LocalDateTime createdAt, String createdBy) implements Auditable {}
Common Mistakes
- Trying to use with Hibernate: ```java // ❌ JPA requires inheritance from base entity @Entity public record User(Long id, String name) {} // doesn’t work
// ✅ Regular class for JPA @Entity public class User { @Id private Long id; private String name; }
2. **Expecting polymorphism:**
```java
// Record does not support polymorphism through class inheritance, but supports
// it through interfaces and sealed types.
public record Circle(double radius) {}
public record Rectangle(double w, double h) {}
// ✅ Use sealed interfaces + pattern matching (Java 21+)
public sealed interface Shape permits Circle, Rectangle {}
public record Circle(double radius) implements Shape {}
public record Rectangle(double w, double h) implements Shape {}
double area(Shape s) {
return switch (s) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.w() * r.h();
};
}
🔴 Senior Level
Internal Implementation
Class file structure:
// Record has ACC_FINAL | ACC_RECORD flags
// super_class always points to java/lang/Record
ClassFile {
access_flags: ACC_FINAL | ACC_RECORD // implicit final
super_class: constant_pool[java/lang/Record]
interfaces_count: N // can implement interfaces
}
JVM specification (JVMS 4.7.31 — Record attribute and JVMS 4.1 — access_flags ACC_RECORD):
- Record must have
ACC_FINALflag - Super class must be
java.lang.Record - Cannot be changed through bytecode manipulation
Architectural Trade-offs
Why Records cannot be extended:
1. Encapsulation violation — subclass can change equals/hashCode behavior
2. hashCode contract issues — different subclasses can have different fields
3. Serialization — difficult to determine canonical form for hierarchy
4. Pattern matching — deconstruction requires fixed structure
Edge Cases
1. Sealed interfaces as alternative:
public sealed interface Event permits UserCreated, UserDeleted, OrderPlaced {}
public record UserCreated(String userId, Instant timestamp) implements Event {}
public record UserDeleted(String userId, String reason) implements Event {}
public record OrderPlaced(String orderId, BigDecimal amount) implements Event {}
// Pattern matching for processing
String process(Event event) {
return switch (event) {
case UserCreated uc -> "User created: " + uc.userId();
case UserDeleted ud -> "User deleted: " + ud.userId();
case OrderPlaced op -> "Order placed: " + op.orderId();
};
}
2. Generic Records:
public record ApiResponse<T>(int status, T data, String message) {}
// Specialization via interfaces
public interface Validated {}
public record ValidatedResponse<T>(ApiResponse<T> response, boolean isValid)
implements Validated {}
3. Composition over inheritance:
public record Pagination(int page, int size) {}
public record Sort(String field, Direction direction) {}
public record PagedRequest(Pagination pagination, Sort sort, String filter) {}
// Instead of:
// public class PagedRequest extends Pagination { Sort sort; String filter; }
Performance
Inheritance vs Composition:
- Inheritance: slightly faster field access (one level)
- Composition: negligible overhead (extra reference)
- JIT inlines both approaches efficiently
- Difference < 1% in real applications
Production Experience
Real case — Event Sourcing:
// Event-driven architecture with sealed + records
public sealed interface DomainEvent {
UUID eventId();
Instant occurredAt();
}
public record UserCreatedEvent(
UUID eventId, Instant occurredAt, String userId, String name
) implements DomainEvent {}
public record UserEmailChangedEvent(
UUID eventId, Instant occurredAt, String userId, String oldEmail, String newEmail
) implements DomainEvent {}
// Processing
public void handle(DomainEvent event) {
switch (event) {
case UserCreatedEvent e -> createUser(e.userId(), e.name());
case UserEmailChangedEvent e -> updateEmail(e.userId(), e.newEmail());
}
}
Best Practices
// ✅ Sealed interfaces + records for polymorphism
public sealed interface Payment permits CashPayment, CardPayment, CryptoPayment {}
public record CashPayment(BigDecimal amount) implements Payment {}
public record CardPayment(BigDecimal amount, String cardNumber) implements Payment {}
public record CryptoPayment(BigDecimal amount, String walletAddress) implements Payment {}
// ✅ Composition for reusability
public record AuditInfo(LocalDateTime createdAt, String createdBy) {}
public record User(String name, String email, AuditInfo audit) {}
// ❌ Don't try to emulate inheritance
// ❌ Don't use Record for JPA entities
// ❌ Don't use Record for mutable state
🎯 Interview Cheat Sheet
Must know:
- Record cannot be extended — implicit final (ACC_FINAL flag)
- Record cannot extend another class — always extends
java.lang.Record - Record can implement interfaces:
record User implements Serializable - Alternative to inheritance — composition or sealed interfaces
- Sealed interfaces + Records + pattern matching = polymorphism replacement
- JVM prohibits extends Record at bytecode level (ACC_RECORD flag)
Common follow-up questions:
- Why can’t Record be extended? — For immutability guarantee, predictable equals/hashCode and JVM optimization
- How to implement polymorphism with Records? — Sealed interfaces + pattern matching (Java 21+)
- Can you use Record with Hibernate? — No, JPA requires inheritance and mutable state
- Why is composition better than inheritance for Records? — Preserves immutability, doesn’t break Record contract
Red flags (DO NOT say):
- ❌ “You can extend Record through an intermediate class” — Prohibited at JVM level
- ❌ “Record inherits Object” — Record inherits
java.lang.Record - ❌ “Record supports polymorphism through inheritance” — Only through interfaces and sealed types
- ❌ “You can use Record for JPA entity hierarchy” — JPA doesn’t support Records
Related topics:
- [[1. What is Record in Java and since which version are they available]]
- [[2. What are the main differences between Record and regular class]]
- [[4. Can you add additional methods to a Record]]
- [[17. What is PECS (Producer Extends Consumer Super)]]