Can You Inherit From Record or Have Record Inherit From Another Class
While class inheritance is forbidden, a Record can implement any number of interfaces:
🟢 Junior Level
No to both. Java imposes two categorical restrictions regarding class inheritance with records:
- You cannot inherit FROM a
Record: Every record is implicitlyfinal. Attempting to declare a class or another record that extends an existing record triggers a compilation error. - A
Recordcannot extend another class: All records implicitly extendjava.lang.Recorddirectly. Because Java does not support multiple class inheritance, using theextendskeyword with a record is prohibited.
public record Point(int x, int y) {}
// ❌ Compilation error: Cannot inherit from final 'Point'
public class ColoredPoint extends Point {}
// ❌ Compilation error: Records cannot extend other classes
public record MyRecord(int a) extends SomeClass {}
// ❌ Compilation error: Records cannot extend java.lang.Record explicitly
public record MyRecord(int a) extends Record {}
What IS Permitted: Implementing Interfaces
While class inheritance is forbidden, a Record can implement any number of interfaces:
public record User(String name, String email) implements Serializable, Comparable<User> {
@Override
public int compareTo(User o) {
return this.name.compareTo(o.name);
}
}
🟡 Middle Level
Architectural Rationale Behind the Restrictions
The ban on class inheritance in Records is a deliberate language design decision by OpenJDK architects:
- State-is-the-Header Principle: The state of a record is defined exclusively and transparently by its header. If subclassing were permitted, a child class could declare additional instance fields, destroying the contract that “the header is the state”.
- Immutability Invariant: A subclass could introduce mutable fields or override accessor methods with stateful or fluctuating behavior.
- Preserving
equals()andhashCode()Contracts: The classic object-oriented equivalence pitfall (detailed in Joshua Bloch’s Effective Java, Item 10) proves that it is mathematically impossible to preserveequals()symmetry and transitivity if subclasses add value components. For records, equality is strictly mathematical: two records are equal if and only if all their header components are equal. - JIT Compiler Optimization: Knowing that records are
finaland structurally immutable allows the HotSpot JIT compiler to aggressively inline accessors, optimize scalar replacement via Escape Analysis, and prepare for future flat value types in Project Valhalla.
Replacing Inheritance: Composition over Inheritance
Instead of class inheritance hierarchies, modern Java relies on composition and delegation:
// ❌ Anti-pattern: Attempting to build an inheritance hierarchy
// record AuditedOrder(...) extends BaseAuditInfo {} // Compilation error!
// ✅ Idiomatic pattern: Component composition
public record AuditMetadata(Instant createdAt, String createdBy) {}
public record Order(
UUID orderId,
BigDecimal amount,
AuditMetadata audit // Embedded immutable component
) {}
🔴 Senior Level
Algebraic Data Types (ADT): Sealed Interfaces + Records
In modern Java (Java 17+), classical hierarchical inheritance for domain models is replaced by Algebraic Data Types (ADTs):
- Product Type: Modeled via
record($A \times B$: an order consists of an ID and an amount). - Sum Type: Modeled via
sealed interface($A + B + C$: a payment method is either card, or cash, or crypto).
public sealed interface PaymentMethod permits CardPayment, CashPayment, CryptoPayment {}
public record CardPayment(String cardNumber, String cvv) implements PaymentMethod {}
public record CashPayment(BigDecimal tenderedAmount) implements PaymentMethod {}
public record CryptoPayment(String walletAddress, String txHash) implements PaymentMethod {}
Exhaustive Pattern Matching in Java 21+
Because the compiler knows all permitted implementations of a sealed interface, switch pattern matching requires no default branch, providing compile-time exhaustiveness checking:
public String processPayment(PaymentMethod method) {
return switch (method) {
case CardPayment(var num, var cvv) -> "Processing card: " + mask(num);
case CashPayment(var amount) -> "Cash received: " + amount;
case CryptoPayment(var addr, var tx) -> "Crypto transfer: " + tx;
// No default needed: compiler guarantees exhaustive coverage
};
}
JVM Specification Constraints (JVMS)
The inheritance ban is enforced at both compiler and JVM runtime levels:
- Access Flags: The compiled bytecode must contain both
ACC_FINALandACC_RECORDflags. - Superclass Requirement: Under JVMS §4.1, the superclass entry in the constant pool must be strictly
java/lang/Record. - Regular Classes Cannot Extend
Record: Under JLS §8.1.4, attempting to compile an ordinary class extendingjava.lang.Recordfails. - ClassLoader Verification: If bytecode manipulation tools (ASM, ByteBuddy) attempt to synthesize an ordinary class inheriting from
java.lang.Record, the JVM verifier rejects the class at link time with aClassFormatErrororVerifyError.
4 Tricky Questions
1. Can a regular Java class explicitly extend java.lang.Record (public class MyClass extends java.lang.Record)?
Answer:
No, this is strictly forbidden by the Java Language Specification (JLS §8.1.4).
The compiler produces the error: "The type java.lang.Record cannot be the superclass of MyClass; only records can extend java.lang.Record".
Furthermore, this constraint is enforced by the JVM bytecode verifier. If manipulated bytecode tries to load a non-record class whose superclass is java/lang/Record, the class loader throws a fatal ClassFormatError or VerifyError.
2. Can a Record be declared with abstract, sealed, or non-sealed modifiers?
Answer:
abstract: Prohibited. Every record is implicitlyfinaland contains complete implementations for all components;final abstractis illegal.sealed/non-sealed: Prohibited. Because a record is final and cannot have subclasses, inheritance control modifiers are meaningless on record declarations. However, a record can serve as a permitted leaf in a sealed interface hierarchy:public sealed interface Result<T> permits Success, Failure {} public record Success<T>(T data) implements Result<T> {} public record Failure<T>(Throwable error) implements Result<T> {}
3. How do Algebraic Data Types (ADTs) using Records and Sealed Interfaces replace traditional class inheritance?
Answer: Traditional class inheritance represents open polymorphism: base classes can be extended arbitrarily across modules, and behavior is dispersed across virtual methods. In contrast, ADTs represent closed type systems:
- A
sealed interfacedefines the closed universe of all possible system states (Sum Type). - A
recorddefines the fixed, immutable structure for each concrete variant (Product Type). - Operations and behaviors are defined externally via pattern matching and pure functions. When a new variant is introduced to the sealed hierarchy, the compiler forces every
switchexpression in the codebase to handle the new case, preventing unhandled edge cases.
4. If two Records have identical components (record PointA(int x, int y) and record PointB(int x, int y)), can they be used interchangeably without a common interface?
Answer:
No, they cannot.
Java is a nominal type system, not a structural (duck-typed) system. Even though PointA and PointB share identical memory layouts, component names, and types, they are two completely distinct, incompatible types in the JVM type system. You cannot assign an instance of PointA to a variable of type PointB without explicit mapping. Polymorphic interchangeability requires declaring a common interface:
public interface Coordinates { int x(); int y(); }
public record PointA(int x, int y) implements Coordinates {}
public record PointB(int x, int y) implements Coordinates {}
🎯 Interview Cheat Sheet
Summary in 60 Seconds
- Record $\to$ Class: A record cannot extend any class (it already implicitly extends
java.lang.Record). - Class $\to$ Record: No class or record can extend a record (all records are implicitly
final). - Class $\to$
java.lang.Record: Regular classes are forbidden from extendingjava.lang.Record. - Interfaces: Records can implement any number of interfaces (
implements InterfaceA, InterfaceB). - Modifiers: Records cannot be
abstract,sealed, ornon-sealed. - Modern Alternative: Use Composition and Algebraic Data Types (
sealed interface+record) with Java 21 Pattern Matching.
Common Interview Traps
- Claiming records can be subclassed if declared non-final (records are always implicitly final).
- Believing
java.lang.Objectis the direct superclass of a record (direct superclass isjava.lang.Record). - Trying to declare an abstract record to share common components across multiple records.