The problem with plain classes
Many classes exist only to hold values: a user with a name and an age, a point with x and y. Kotlin lets you declare such a class in one line, with the properties in the primary constructor:
class User(val name: String, val age: Int)
A plain class stores that data but doesn't know how to show it or compare it. Two defaults get in the way:
toStringprints the class name and a hash code instead of the properties.==callsequals, which for a plain class asks whether both sides are the same object, not whether they hold the same values.
So this code surprises beginners:
val a = User("Ana", 25)
val b = User("Ana", 25)
println(a)
println(a == b)
It prints a line like User@1540e19d (the part after @ varies) and then false. Two users built from identical values are "not equal", because they are two separate objects.
The fix: the data keyword
Put data in front of class and the compiler writes the useful versions for you, based on the properties in the primary constructor:
data class User(val name: String, val age: Int)
println(User("Ana", 25))
println(User("Ana", 25) == User("Ana", 25))
User(name=Ana, age=25)
true
A data class gets these generated members:
toString, which lists the class name and every property asname=value.equalsandhashCode, which compare and hash the property values. That is why equal data gives==atrueresult, and why equal objects can be found in aHashSetor used asHashMapkeys.copy, which makes a new object and lets you change some properties.componentNfunctions, which allow destructuring:val (n, a) = anagivesn = "Ana"anda = 25.
The identity check is still available: === asks whether two references point to the very same object. For two separately built users it is false, even when == is true.
copy: a changed version without changing the original
When properties are val, an object can't be modified after it is built. To get a variation, call copy and name only what should differ:
val ana = User("Ana", 25)
val bob = ana.copy(name = "Bob")
println(bob)
println(ana)
User(name=Bob, age=25)
User(name=Ana, age=25)
The age carries over, and ana is untouched. Properties you leave out keep their current values, and ana.copy() with no arguments makes an equal duplicate.
Rules and gotchas
- Every primary-constructor parameter must be a property. Writing
data class User(name: String)withoutvalorvaris a compile error:primary constructor of data class must only have property ('val' / 'var') parameters. - Only constructor properties count. A property declared inside the class body is left out of
toString,equals,hashCodeandcopy. Two objects that differ only in such a property are still equal. copyis shallow. If a property holds a mutable list, the copy and the original share that same list, so changing the list through one shows in the other.- Prefer
val. A data class may usevarproperties, but changing a property of an object that sits in aHashSetor is aHashMapkey changes its hash code, and the collection can no longer find it. Read-only properties pluscopyavoid the problem.
The exercise
The lesson asks for a program that shows all three generated members and prints exactly this:
ana = User(name=Ana, age=25)
same = true
bob = User(name=Bob, age=25)
The steps:
- Declare the data class at the top, with
val name: Stringandval age: Int. One line is enough, because a data class needs no body. - Create
anaby calling the constructor like a function. Kotlin has nonewkeyword. - Print her. Inside a string template,
$anacalls the generatedtoString. - Create
twinfrom the same name and age. It is a separate object. - Print the comparison. An expression inside a template needs braces:
${ana == twin}. Without them,$ana == twinwould insertanaand leave the rest as plain text. - Derive
bobwithana.copy(name = "Bob")and print it like the first user.
data class User(val name: String, val age: Int)
val ana = User("Ana", 25)
println("ana = $ana")
val twin = User("Ana", 25)
println("same = ${ana == twin}")
val bob = ana.copy(name = "Bob")
println("bob = $bob")
Key takeaways
- Add
datato a class that mainly holds values to gettoString,equals,hashCode,copyand destructuring. - With a data class,
==compares property values;===compares object identity. - Use
copy(...)to build a modified version of an immutable object. - Only the primary-constructor properties take part, and each must be marked
valorvar.