Which class modifiers are forbidden on a data class (abstract, open, sealed, inner), and why?
answer
- Forbidden: abstract, open, sealed, inner
- Data classes are implicitly final
- Final protects equals/hashCode + Liskov
- inner forbidden — hidden outer reference
- Interfaces and nested (non-inner) are OK
basics
~10 sA 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 sKotlin 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// 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 innergo deeper
Recalls that abstract/open/sealed/inner are not allowed on data classes.
Explains that data classes are implicitly final and can still implement interfaces and be nested.
Articulates the equals/hashCode contract and Liskov rationale for finality, and the captured-outer reason for the inner ban.
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