Data Classes

Marking a class data generates a readable toString, content-based equality, and a copy() method for making a modified duplicate of an immutable object.

What data class adds

A plain class only gets a default toString() that prints something unhelpful like Point@1b6d3586, and its default equals() compares references, not contents. Marking a class data generates all of that properly, based on the properties in its primary constructor:

</> main.kt
data class Point(val x: Int, val y: Int)

fun main() {
    val p1 = Point(2, 3)
    val p2 = Point(2, 3)

    println(p1)
    println(p1 == p2)
}
Output
Point(x=2, y=3)
true

println(p1) prints a readable representation automatically, and p1 == p2 is true because data class generates a content-based equals() — two different Point objects with the same coordinates are considered equal.

copy() for modified duplicates

Data classes also get a copy() method, which duplicates the object while letting you override just the properties you name:

</> main.kt
data class Point(val x: Int, val y: Int)

fun main() {
    val original = Point(2, 3)
    val moved = original.copy(x = 5)

    println(original)
    println(moved)
}
Output
Point(x=2, y=3)
Point(x=5, y=3)

moved is a completely new object with x changed to 5 and y carried over unchanged from original — a common pattern for working with immutable data without hand-writing a new constructor call every time only one field changes.

Note: The generated equals()/hashCode()/copy() only consider properties declared in the primary constructor. A property added inside the class body (not the constructor parameter list) is invisible to all of them — an easy mistake when refactoring a data class and moving a property in or out of the constructor.