Interview QuestionPractical QuestionFollow-up Questions

Data Classes and Generated Methods

skydovesJaewoong Eum (skydoves)||9 min read

Data Classes and Generated Methods

Kotlin data classes are a language feature that instructs the compiler to generate equals(), hashCode(), toString(), copy(), and componentN() functions based on the properties declared in the primary constructor. Understanding what the compiler produces, which properties participate in the generated code, and where data classes fall short is necessary for writing correct domain models. By the end of this lesson, you will be able to:

  • Describe the exact methods the Kotlin compiler generates for a data class and which properties they use.
  • Explain how equals() and hashCode() behave for properties declared inside versus outside the primary constructor.
  • Identify the behavioral differences between copy() and manually constructing a new instance.
  • Recognize situations where data classes introduce subtle bugs in collections and maps.
  • Compare data classes against regular classes for modeling mutable or behavior heavy objects.

Generated Methods and Primary Constructor Properties

When you mark a class with the data keyword, the compiler generates five methods. All of them operate exclusively on the properties declared in the primary constructor:

data class User(val name: String, val age: Int)

For this declaration, the compiler generates:

  • equals() that compares name and age using structural equality.
  • hashCode() that combines the hash codes of name and age.
  • toString() that returns "User(name=..., age=...)".
  • copy() that creates a new instance with the option to override specific properties.
  • component1() and component2() for destructuring declarations.

Properties declared in the class body are excluded from all generated methods. This is an intentional design decision, not a bug:

data class User(val name: String, val age: Int) {
    var email: String = ""
}

val a = User("Alice", 30).apply { email = "a@test.com" }
val b = User("Alice", 30).apply { email = "b@test.com" }
println(a == b) // true, email is ignored

The email property does not appear in equals(), hashCode(), toString(), or copy(). If you need a property to participate in equality checks, it must be in the primary constructor.

equals() and hashCode() Under the Hood

The generated equals() method performs a reference check first, then a type check, and finally compares each constructor property using == (which calls equals() on each property). The decompiled bytecode for User looks roughly like this:

override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is User) return false
    return name == other.name && age == other.age
}

override fun hashCode(): Int {
    var result = name.hashCode()
    result = 31 * result + age
    return result
}

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor