Often, we create a class to just hold some data and set of functions that help us to use and manipulate those data. In Kotlin, we can declare such a class as a data class. The compiler will then generate a set of functions on our behalf. 

Let’s see an example of a data class. 

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

The following section shows all the functions generated by the compiler when we declare our class as a data class.

  • equals() / hashCode(): hashCode() returns the hash code of an object of the data class and equals() takes the hashcode to determine if the two objects are equal.
  • toString(): returns the string representation of an object. 
  • componentN(): returns the properties based on the order of their declaration. If there are two properties in the data class as shown in the example, then you will have component1() that would return name and component2() that would return age.
  • copy(): returns the copy of an object. While using this method we can also modify one or all of the properties while copying the object.

Rules of data class

There are a set of rules that we have to follow when declaring data class. 

  1. Data class constructor must have at least one parameter.
  2. All primary constructor parameters are marked as val or var.
  3. Data class cannot be open, sealed, abstract, or inner.
  4. Data class may only implement an interface.

Example

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

fun main(args: Array<String>) {
    val user1 = User("Sonia", 21)
    val user2 = User("Paul", 24)
    val user3 = User("Sonia", 21)

    println(user1.equals(user2))
    println(user1.equals(user3))

    println(user1.hashCode())
    println(user2.hashCode())
    println(user3.hashCode())

    println(user1.component1())
    println(user1.component2())

    val user4 = user2.copy(name = "Sonia")
    println(user4.toString())
}

Output

As you can see in the example, the declaration of the data class follows all the rules specified above. It also includes all the methods that are generated by our compiler. 

Define your own function

If we want to use our own function to compute equality or hashcode then we can provide our own functions. When we define our own function, then the compiler will not generate the method for us, instead, it uses our function to perform any computation.

data class User(val name: String, val age: Int) {
    override fun equals(other: Any?): Boolean {
        return (other as User).name == this.name
    }

    override fun hashCode(): Int {
        return name.hashCode()
    }
}

fun main(args: Array<String>) {
    val user1 = User("Sonia", 21)
    val user2 = User("Sonia", 85)

    println(user1.equals(user2))

    println("Hash code of user1: ${user1.hashCode()}")
    println("Hash code of user1: ${user2.hashCode()}")
}

Output

As you can see in the example above the equal function depends only on the name of the User. That’s why we are getting the User(“Sonia”, 21) and User(“Sonia”, 85) as equal.

Properties declared in the class body

The compiler generates methods using all the parameters passed in the constructor. But if you do not want to use only some of the variables in the parameters in the generated functions like equals or hashcode then we can declare the variables as a class body.

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

fun main(args: Array<String>) {
    val user1 = User("Sonia")
    val user2 = User("Sonia")
    user1.age = 21
    user2.age = 85

    println(user1.equals(user2))

    println("Hash code of user1: ${user1.hashCode()}")
    println("Hash code of user1: ${user2.hashCode()}")
}

In the example, the property age is declared in the class body, so age is not used in the generated method like equal or hashcode.

Destructuring Declaration

In Kotlin, we can create multiple variables that can store the value of the parameters passed in the constructor of a data class. Such a declaration is called the Destructuring declaration. Line 5 is an example of a destructuring declaration. The value of the constructor property name is assigned to user_name and age is assigned to user_age.

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

fun main(args: Array<String>) {
    val user = User("Sonia", 21)
    val (user_name, user_age) = user

    println("The name of the user is $user_name and age is $user_age")
}

The data class helps us to prevent from writing boilerplate code and makes the code very concise. With the help of a destructuring declaration, working with the properties of a data class is also very convenient. Click on the next button to continue reading.

Category
Tags

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *