kotlin data classes

Kotlin Data Classes: Why, What and How?

In this post on Kotlin’s data classes, we’ll take a look at how data classes are better than regular Java POJO (Plain Old Java Object) classes, and how they make our everyday lives easier. We’ll also take a look at some of the caveats of data classes.

 

I’ve also written an article on Java vs Kotlin, if you’re interested, check it out here:

Boilerplate!

Have a look at this Java class:

class Dog{
    private String name;
    private String sound;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSound() {
        return sound;
    }

    public void setSound(String sound) {
        this.sound = sound;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

 

All of this ceremony for a simple Java POJO class used to hold some data. You might have used such classes when serializing/deserializing a JSON response from an API. It provides you getters and setters for the properties.

 

Now let’s see how it’s done in Kotlin with the help of data classes:

data class Dog(var name: String, var age: String, var sound: String)

 

Just a single line! We can go ahead and use this class in the same way as the java class. Getters and setters are compiler generated for us.

Even the methods such as toString(), hashCode() and equals() are compiler generated. Also, methods such as componentN and copy are generated but they have a caveat. We’ll talk about them in upcoming sections.

 

 

Inheritance in Data Classes 

Data classes are final by default. They cannot be abstract, open, sealed or inner. They can only inherit from other non-data classes (eg. sealed classes after 1.1,  before 1.1 data classes can only implement interfaces).

Data classes can override properties and methods from the interfaces they implement.

interface Animal{
    val name:String
    fun sound()
}

data class Dog(override val name:String):Animal{

    override fun sound(){
        println("Barks")
    }
}

 

We know that hashCode, toString and equals are auto generated for data class. But we can explicitly provide our own implementations of these in data class body. In such case, the explicit implementations are used.

 We cannot provide explicit implementations of componentN() and copy() functions.

 

ComponentN() functions in data classes

Component functions are used for destructive declarations. What it means is that we can do something like this:

data class Dog(var name: String, var age: String)

val dog = Dog("Tom", "1")

var (name, age) = dog //destructive declaration

 

It is not possible to provide an explicit implementation for componentN functions, these are generated by the compiler implicitly.

componentN functions are also a reason why data classes cannot inherit from other data classes. Quoting an engineer from the JetBrains team:

“You can inherit a data class from a non-data class. Inheriting a data class from another data class is not allowed because there is no way to make compiler-generated data class methods work consistently and intuitively in case of inheritance.”

 

For example, the following code would give an error due to clash of component1 functions:

open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()

 

Copy() function

It is possible to create a clone of a data class in kotlin. When using copy, a shallow copy of the object is created.

data class Dog(var name: String, var age: String)

val dog = Dog("Tom", "1")

var dog2 = dog.copy()

 

It is also possible to pass named parameters to copy function. It’s useful when you want to alter some properties while cloning, which is a frequent use case.

data class Dog(var name: String, var age: String)

val dog = Dog("Tom", "1")

var dog2 = dog.copy(name = "Jack")

 

Just as with componentN functions, it’s not possible to provide explicit implementation of copy.

 

Caveats 

Auto generated methods work only for primary constructor params.

Let’s have a look at this example:

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

var dog1 = Dog("Tom")
var dog2 = dog1.copy()

dog1.age = 1
dog2.age = 2

println(dog1.equals(dog2))

 

What do you expect to be printed on the console? Logically, since age of the Dogs are different, they should be different. Let’s have a look at the output:

data classes kotlin

 

Whoa! They are exactly the same. This happens because hashCode, toString and equals method only work on the constructor parameters of data class. So, when we check for equality, it compares the names of the animals and returns true.

According to documentation, compiler only uses properties inside primary constructor to generate functions. Component functions are also created only for primary constructor parameters. 

  • Primary constructor needs to have at least one parameter and all the fields must be marked var or val. To have a parameterless constructor, provide default values to all the parameters.
data class Dog(var name: String = "Tom", var age: String = "1")

var dog = Dog()
  • The generated componentN functions will override any similar implementation of componentN function in supertype.
  • Triple and Pair are standard data-classes but it’s better to use named data classes as they make the code more readable.

Conclusion

Data classes are one of the most useful features of Kotlin. Many Android Development interviews also include some questions on Kotlin and data classes are one of the focused topics.

 

*Important*: Join the AndroidVille SLACK  workspace for mobile developers where people share their learnings about everything latest in Tech, especially in Android Development, RxJava, Kotlin, Flutter, and mobile development in general.

Click on this link to join the workspace. It’s absolutely free!

Like what you read? Don’t forget to share this post on FacebookWhatsapp, and LinkedIn.

You can follow me on LinkedInQuoraTwitter, and Instagram where I answer questions related to Mobile Development, especially Android and Flutter.

If you want to stay updated with all the latest articles, subscribe to the weekly newsletter by entering your email address in the form on the top right section of this page.

About the author

shares