If a data class extends a superclass that defines `toString()`/`equals()`, or you write your own `toString()` in the data class, which one wins — the generated or the explicit one? How does inheritance interact with the generated members?
answer
- Explicit member wins; rest still generated
- Generated uses only own constructor props
- Superclass final member -> compile error
- Inherited state ignored by generated equals
- Keep data classes flat value holders
basics
~10 sIf you write your own equals, hashCode, or toString inside the data class, the compiler uses yours instead of generating that one. The compiler only fills in the members you didn't provide.
solid answer
~40 sThe compiler generates `equals`/`hashCode`/`toString` only for members you did NOT explicitly declare in the data class body. An explicit override always wins for that specific member; the others are still generated. For inheritance, if a non-`final` superclass member exists, the generated implementation overrides it normally — but the generated `equals`/`hashCode` are based solely on the data class's own primary-constructor properties and don't consult superclass state. If the superclass already provides a `final equals`/`hashCode`/`toString`, the compiler will not generate the conflicting one (it errors if generation is impossible). This is why data classes are best as flat value holders; inheriting equality from a base class is fragile.
code
kotlin · 5 linesdata class User(val name: String, val age: Int) {
override fun toString() = "User#$name" // wins
}
println(User("Ann", 30)) // User#Ann
println(User("Ann", 30) == User("Ann", 31)) // false (generated equals uses name+age)go deeper
Knows an explicit toString/equals you write replaces the generated one.
Adds that only the unspecified members are generated and they use constructor properties.
Explains that generated members ignore superclass state and that final base members cause conflicts.
Articulates the equals-across-hierarchy pitfall and argues for flat value holders / composition / sealed types.
## The override rule For each of `equals`, `hashCode`, `toString`, the compiler generates it **only if you did not declare it yourself** in the data class body. Your explicit implementation always takes precedence for that member; the remaining ones are still auto-generated. ```kotlin data class Money(val cents: Long) { override fun toString(): String = "$%.2f".format(cents / 100.0) // equals & hashCode still generated from cents } println(Money(150)) // $1.50 (your toString wins) println(Money(150) == Money(150)) // true (generated equals) ``` ## Interaction with superclasses Data classes can extend an open class or implement interfaces. Key points: - Generated `equals`/`hashCode`/`toString` use **only the data class's own primary-constructor properties** — they never read superclass fields. So a base class with its own state isn't reflected in equality. - If the superclass declares one of these members as **`final`**, the compiler cannot override it; if it needs to generate that member, you'll get a compile error. You must then either remove `final` upstream or provide a compatible setup. - If the superclass declares an **`open`/abstract** `toString()` etc., the generated member simply overrides it, like any normal override. ```kotlin abstract class Base { abstract val id: String } data class Item(override val id: String, val qty: Int) : Base() // equals/hashCode/toString use id and qty (both are constructor props) ``` ## Why flat is better Because generated equality ignores inherited state and isn't symmetric across a base/derived split, mixing inheritance with generated equality breaks the symmetric/transitive contract (the classic 'equals across a class hierarchy' problem). The idiomatic guidance is to keep data classes as **flat value holders** and prefer composition or `sealed` hierarchies over deep inheritance. ## Quick reference - You declare it -> compiler keeps yours. - You don't -> compiler generates from constructor props. - Superclass `final` member that conflicts -> compile error. - Generated members never include superclass state.
- Does the generated equals include a superclass's properties?No. It uses only the data class's own primary-constructor properties; superclass state is ignored.
- What if the base class marks toString() as final?The compiler can't override it; if it would need to generate toString, you get a compile error and must resolve the conflict.
saying these in an interview costs you the question
- Thinks the generated member overrides your explicit one
- Believes generated equals walks the superclass hierarchy
- Unaware that a final superclass member causes a compile error
- Advocates deep inheritance for data classes despite the equals-across-hierarchy problem