English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Kotlin Data Classes (Data)

In this article, you will learn how to create data classes in Kotlin. You will also learn about the requirements that data classes must meet and their standard features.

There may be situations where you need to create a class that is used only for storing data. In this case, you can mark the class as data to create a data class. For example:

data class Person(val name: String, var age: Int)

For this type, the compiler will automatically generate:

  • copy() function, equals() and hashCode() methods, as well as the toString() form of the primary constructor

  • componentN() function

Before we delve into these functions, let's discuss the requirements that a data class must meet.

Requirements for Kotlin data classes

The requirements are as follows:

  • The primary constructor must have at least one parameter.

  • The parameters of the primary constructor must be marked as val (read-only) or var (read-write).

  • The class cannot be open, abstract, inner, or sealed.

  • This class can extend other classes or implement interfaces. If you are using1.1Previous versions of Kotlin allowed this class to implement only interfaces.

Example: Kotlin data class usage

data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
    val jack = User("jack", 29)
    println("name = ${jack.name}")
    println("age = ${jack.age}")
}

The output when running the program is:

name = jack
age = 29

When you declare a data class, the compiler automatically generates several functions in the background, such as toString(), equals(), hashCode(), etc. This helps to keep the code concise. If you use Java, you need to write a lot of boilerplate code.
Let's use the following function:

The copy() function - Copy

For data classes, you can use the copy() function to create a copy of an object with different properties. It works as follows:

data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
    val u1 = User("John", 29)
   
    //Use the copy function to create an object
    val u2 = u1.copy(name = "Randy")
    println("u1: name = ${u1.name}, name = ${u1.age")
    println("u2: name = ${u2.name}, name = ${u2.age")
}

The output when running the program is:

u1: name = John, name = 29
u2: name = Randy, name = 29

The toString() method - Return a string

The toString() function returns the string representation of the object.

data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
    val u1 = User("John", 29)
    println(u1.toString())
}

The output when running the program is:

User(name = John, age =29)

hashCode() and equals()

The hasCode() method returns the hash code of the object. If two objects are equal, hasCode() will produce the same integer result.

If two objects are equal (hashCode() is the same), equals() returns true. If the objects are not equal, equals() returns false.

data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
    val u1 = User("John", 29)
    val u2 = u1.copy()
    val u3 = u1.copy(name = "Amanda")
    println("u1 hashCode = ${u1.hashCode()")
    println("u2 hashCode = ${u2.hashCode()")
    println("u3 hashCode = ${u3.hashCode()")
    if (u1.equals(u2) == true)
        println("u1 equal to u2.
    else
        println("u1 not equal to u2.
    if (u1.equals(u3) == true)
        println("u1 equal to u3.
    else
        println("u1 not equal to u3.
}

The output when running the program is:

u1 hashCode = 71750738
u2 hashCode = 71750738
u3 hashCode = 771732263
u1 equal to u2.
u1 not equal to u3.

Destructuring Declaration

You can use destructuring declarations to decompose an object into multiple variables. For example:

data class User(val name: String, val age: Int, val gender: String)
fun main(args: Array<String>) {
    val u1 = User("John", 29, "Male")
    val (name, age, gender) = u1
    println("name = $name")
    println("age = $age")
    println("gender = $gender")
}

The output when running the program is:

name = John
age = 29
gender = Male

This is possible because the compiler generates the component() function for all properties of the data class. For example:

data class User(val name: String, val age: Int, val gender: String)
fun main(args: Array<String>) {
    val u1 = User("John", 29, "Male")
    println(u1.component1())     // John
    println(u1.component2())     // 29  
    println(u1.component3())     // "Male"
}

The output when running the program is:

John
29
Male