Kotlin Data Classes

This tutorial is about Kotlin Data classes, requirements that data class must fulfill, and their standard functionalities.

Data Classes

Data classes  are classes whose main purpose is to hold data/state and contains standard functionalities. A data keyword is used to declare a class as a data class.

Example:

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

Data class internally contains the following functions:

  • equals(): Boolean
  • hashCode(): Int
  • toString(): String
  • component() functions corresponding to the properties
  • copy()

Due to presence of above functions internally in data class, the data class eliminates the boilerplate code.

Data Class Example
data class Student(val name: String, val age: Int)

fun main() {
    val s1 = Student("Rocky", 29)
    println("name = ${s1.name}")
    println("age = ${s1.age}")
}

Output:

name = Rocky
age = 29

When you declare a data class, the compiler automatically generates several functions such as toString()equals()hashcode() etc behind the scenes.

Data Class Requirements

  • The primary constructor must have at least one parameter.
  • The parameters of the primary constructor must be marked as either val (read-only) or var (read-write).
  • The class cannot be open, abstract, inner or sealed.
  • The class may extend other classes or implement interfaces. If you are using Kotlin version before 1.1, the class can only implement interfaces.

Properties declared in the class body

The compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body:

data class Student(val name: String) {
    var age: Int = 0
}

Only the property name will be used inside the toString(), equals(), hashCode(), and copy() implementations, and there will only be one component function component1().

Let’s understand these functions:

toString() method

Let’s see a simple program without data class.

Example:

class Student(val name: String, val age: Int)

fun main() {
    val s1 = Student("Rocky", 29)
    println(s1)
}

Here, we are printing the reference of Student object which displays the hashCode() with class name of Student. It does not print the data.

Output:

Student@49476842

The above program is rewritten using data class and printing the reference of Student class and displaying the data of object. It happens because the data class internally contains the toString() which display the string representation of object.

Example:

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

fun main() {
    val s1 = Student("Rocky", 29)
    println(s1)
//or
    println(s1.toString())
}

Output:

Student(name=Rocky, age=29)
Student(name=Rocky, age=29)

copy() method

The data class provides a copy() method which is used to create a copy of object. Using copy() method, some or all properties of object can be altered.

Example:

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

fun main() {
    val s1 = Student("Wilson", 29)

    // using copy function to create an object
    val s2 = s1.copy(name = "Rocky")

    println(s1)
    println(s2)
}

Output:

Student(name=Wilson, age=29)
Student(name=Rocky, age=29)

hashCode() and equals()

hashCode(): returns a hash code value for the object.

  • Two hash codes declared two times on same object will be equal.
  • If two objects are equal according to equals() method, then the hash codes
    returned will also be same.

equals(): return true if two objects have same contents and it works similar to “==”.

Example:

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

fun main() {
    val s1 = Student("Rocky", 29)
    val s2 = s1.copy()
    val s3 = s1.copy(name = "Wilson")

    println("s1 : $s1 hashcode : ${s1.hashCode()}")
    println("s2 : $s2 hashcode : ${s2.hashCode()}")
    println("s3 : $s3 hashcode : ${s3.hashCode()}")

    if (s1.equals(s2) == true)
        println("s1 is equal to s2.")
    else
        println("s1 is not equal to s2.")

    if (s1.equals(s3) == true)
        println("s1 is equal to s3.")
    else
        println("s1 is not equal to s3.")
}

Output:

s1 : Student(name=Rocky, age=29) hashcode : -1841810167
s2 : Student(name=Rocky, age=29) hashcode : -1841810167
s3 : Student(name=Wilson, age=29) hashcode : -1282221179
s1 is equal to s2.
s1 is not equal to s3.

Destructuring Declarations

You can destructure an object into a number of variables using destructing declaration.

Example:

data class Student(val name: String, val age: Int, val gender: String)

fun main() {
    val s1 = Student("Rocky", 29, "Male")

    val (name, age, gender) = s1
    println("name = $name")
    println("age = $age")
    println("gender = $gender")
}

Output:

name = Rocky
age = 29
gender = Male

This is possible because the compiler generates componentN() functions for all properties of a data class (defined inside the primary constructor).

Example:

data class Student(val name: String, val age: Int, val gender: String)

fun main() {
    val s1 = Student("Rocky", 29, "Male")

    println(s1.component1())     // Rocky
    println(s1.component2())     // 29  
    println(s1.component3())     //Male
}

Output:

Rocky
29
Male

 

Leave a Reply