Data Classes
The data class declaration that generates equals, hashCode, toString, and copy for classes that primarily hold values.
2 phút đọc
A huge share of the classes in any real program don't do much besides hold a group of related values — a User, a Point, an ApiResponse. Kotlin has a dedicated declaration for exactly this case: data class.
What data adds
data class User(val name: String, val age: Int)
val user1 = User("Alice", 30)
val user2 = User("Alice", 30)
println(user1) // User(name=Alice, age=30)
println(user1 == user2) // true — compares by value, not reference
println(user1.hashCode() == user2.hashCode()) // trueAdding the data keyword generates several things automatically, based on the properties in the primary constructor:
- A readable
toString()—User(name=Alice, age=30)instead of Java's defaultUser@1b6d3586. - Structural
equals()andhashCode()— two instances with the same property values are==, and behave correctly asMapkeys or in aSet. - A
copy()function, covered next. component1(),component2(), etc., which power destructuring, also covered below.
Writing all of that by hand for a plain class would take a dozen-plus lines and be an easy place to introduce a bug (forgetting to update equals() after adding a new property is a classic one). data class generates it correctly, every time, from a single line.
copy(): changing one field, immutably
copy() creates a new instance with the same values as the original, except for whichever properties you explicitly override:
data class User(val name: String, val age: Int)
val original = User("Alice", 30)
val olderAlice = original.copy(age = 31)
println(original) // User(name=Alice, age=30) — unchanged
println(olderAlice) // User(name=Alice, age=31)This is the idiomatic way to "update" an immutable value: rather than making age a var and mutating it in place, you produce a new object that represents the updated state, while the original remains untouched. That pattern matters a lot once you're passing data between threads or through a UI framework that expects immutable state.
Destructuring
Because a data class generates componentN() functions, its properties can be unpacked into separate variables in one line:
val user = User("Alice", 30)
val (name, age) = user
println("$name is $age") // Alice is 30This also works naturally when looping over a list of data class instances:
val users = listOf(User("Alice", 30), User("Bob", 25))
for ((name, age) in users) {
println("$name: $age")
}When not to use data class
A data class is meant for classes whose identity is their data — two User("Alice", 30) instances are meant to be interchangeable. If a class represents something with a distinct identity beyond its current field values (a database connection, a running task), a regular class is the better fit — value-style equality would be misleading there.
Data classes are one of the two Kotlin features most closely associated with the language — the next lesson covers Kotlin's approach to the other pillar of object-oriented programming: inheritance and interfaces.