Question 3 · Section 20

Can you inherit from Record or have Record inherit from another class

Records are created for simple data transfer, not for object-oriented inheritance.

Language versions: English Russian Ukrainian

🟢 Junior Level

No, you cannot. Records in Java have two strict restrictions:

  1. Record cannot be extended — Record is implicit final
  2. 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:

  1. Immutability guarantee — if Record could be extended, a subclass could add mutable fields
  2. Predictable behaviorequals(), hashCode() always work the same way
  3. JVM optimization — JIT can make assumptions about structure
  4. 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

  1. 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_FINAL flag
  • 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)]]