skip to content

Which class modifiers are forbidden on a data class (abstract, open, sealed, inner), and why?

level: middleimportance: must knowfreq 55%

answer

  1. Forbidden: abstract, open, sealed, inner
  2. Data classes are implicitly final
  3. Final protects equals/hashCode + Liskov
  4. inner forbidden — hidden outer reference
  5. Interfaces and nested (non-inner) are OK

basics

~10 s

A data class cannot be abstract, open, sealed, or inner. It is implicitly final and standalone. You can still make it implement interfaces or be a member of (but not inner to) another class.

solid answer

~40 s

Kotlin forbids the `abstract`, `open`, `sealed`, and `inner` modifiers on a `data class`. The first three are all about inheritance: a data class is implicitly `final` and may not be subclassed or be an abstract base, because the compiler-generated `equals`/`hashCode` rely on the exact concrete type and would break Liskov substitutability if subclasses added state. `inner` is forbidden because an inner class captures an outer-instance reference, which would silently leak into the value-semantics of the data class and complicate the generated members. A data class **may** implement interfaces, be `private`/`internal`, be `annotation`-free or annotated, be a nested (non-inner) class, and (since Kotlin 1.1) be local. It can also be a `value`/inline class only via `@JvmInline value class`, which is a separate construct.

code

kotlin · 10 lines
kotlin
// All four fail to compile:
// abstract data class A(val x: Int)
// open     data class B(val x: Int)
// sealed   data class C(val x: Int)
// class O { inner data class D(val x: Int) }

interface Named { val name: String }
data class Person(override val name: String) : Named // OK: implements interface

class Outer { data class Nested(val n: Int) }          // OK: nested, not inner

go deeper

for a junior

Recalls that abstract/open/sealed/inner are not allowed on data classes.

for a middle

Explains that data classes are implicitly final and can still implement interfaces and be nested.

for a senior

Articulates the equals/hashCode contract and Liskov rationale for finality, and the captured-outer reason for the inner ban.

for a principal

Proposes the sealed-interface + final-data-class-leaf pattern for value hierarchies and weighs it against generated-member contracts in API design.

## The four forbidden modifiers A `data class` declaration **cannot** carry any of these modifiers: - `abstract` - `open` - `sealed` - `inner` Each causes a compile error such as `Modifier 'open' is incompatible with 'data'`. ## Why the inheritance modifiers are banned (abstract / open / sealed) A data class is implicitly **final**. The reason is the auto-generated `equals()`/`hashCode()`: - The generated `equals` checks the runtime type and compares the primary-constructor properties. - If a data class could be `open`/`abstract`/`sealed`, a subclass could add new state that `equals`/`hashCode` would ignore, violating the equals contract (symmetry/transitivity) and the **Liskov substitution principle**. Kotlin sidesteps this whole class of bugs by simply making data classes non-inheritable. Note: - `sealed` is also about a closed inheritance hierarchy, so it is equally incompatible with a final class. - A data class **can implement interfaces** (interfaces carry no state), and it **can extend** the implicit `Any` only. ```kotlin abstract data class A(val x: Int) // ERROR open data class B(val x: Int) // ERROR sealed data class C(val x: Int) // ERROR ``` ## Why `inner` is banned An `inner` class holds an implicit reference to its enclosing instance (`outer`). Combining that hidden outer reference with auto-generated value semantics is problematic — the captured outer would not be part of the declared properties yet would affect identity/lifetime. So data classes may not be `inner`. A **nested** (non-inner) data class inside another class is perfectly fine: ```kotlin class Outer { data class Nested(val x: Int) // OK: nested, not inner // inner data class Bad(val x: Int) // ERROR } ``` ## What IS allowed - Implementing interfaces: `data class P(val x: Int) : Comparable<P> { ... }`. - Visibility modifiers: `private`, `internal`, `protected` (where legal). - Being a nested class, a local class, or a top-level class. - Being `@JvmInline value class` is a *separate* feature — a value class is not a data class, though both generate value-based members. ## Key takeaway Data classes trade away inheritance to guarantee correct, predictable value semantics from their generated members.

  • If you need a hierarchy of value types, what's the Kotlin idiom instead of an open data class?
    Use a sealed interface (or sealed class) as the common parent and make each leaf a separate final data class implementing it.
  • Can a data class be the subclass in a hierarchy?
    It can implement interfaces, but it cannot extend another (open/abstract) class. Its only superclass is the implicit Any.

A data class is a sealed-shut value capsule: you can plug it into interface sockets, but you can't extend it or weld it to an outer object.

saying these in an interview costs you the question

  • Claiming data classes can be open or abstract
  • Saying a data class can be inner
  • Not knowing data classes are implicitly final
  • Thinking implementing an interface counts as 'extending' a class
  • Unable to explain the equals/hashCode/Liskov rationale

context