Kotlin

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:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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:

1
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.

01
02
03
04
05
06
07
08
09
10
11
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:

1
2
3
4
5
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:

1
2
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.

1
2
3
4
5
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.

1
2
3
4
5
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:

01
02
03
04
05
06
07
08
09
10
11
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:

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.
1
2
3
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.

Published on Java Code Geeks with permission by Ayusch Jain, partner at our JCG program. See the original article here: Kotlin Data Classes: Why, What and How?

Opinions expressed by Java Code Geeks contributors are their own.

Ayusch Jain

Ayusch is a Software Engineer currently working in Android Development. He's worked long enough that he's transformed into an Android himself :P. Additionally, he also maintains a community of Android developers called: AndroidVille and writes about Android on his website: https://ayusch.com
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button