DEV Community

Pavol Rajzak
Pavol Rajzak

Posted on

On immutability in Kotlin

Kotlin has direct support for immutable data structures. They are called data classes and they have some really nice features.

First of all, the data class creation is really simple. For instance, this User data class will create class with one full-args constructor, hashCode, equals and toString methods generated:

data class User(
        val firstName: String,
        val lastName: String,
        val age: Int
)
Enter fullscreen mode Exit fullscreen mode

There's additional advantage here, that by using val the references to primitive properties are final - thus, cannot be changed. You also need to define all constructor parameters when creating new instance, e.g. User("John", "Doe", 50). That's a good start for building immutable data structures on your own!

The most basic operation you'll want to do with immutable data structure is creating new instance out of existing one and changing (some of) its parameters. This is nicely supported in Kotlin by copy() function.

Let's start by simply copying the instance:

val user1 = User("John", "Doe", 50)
val user2 = user1.copy()

user1 === user2 // referential equality = false
user1 == user2  // structural equality = true
Enter fullscreen mode Exit fullscreen mode

You can see that it works as expected - objects are equal, but instances are not the same. What if we want to change one property?

val user1 = User("John", "Doe", 50)
val user2 = user1.copy(age = 20)
Enter fullscreen mode Exit fullscreen mode

This will copy the instance and change age parameter to 20. Compared to Java (in which cloning/copying without 3rd party library is nearly impossible) this is really awesome!

Let's look at a bit more complex example where we don't use only primitive types but reference to a object instance:

data class System(
        val name: String,
        val user: User
)

val user = User("Jane", "Doe", 50)
val system1 = System("Blog", user)

val system2 = system1.copy()

system1 === system2             // false
system1.user === system2.user   // true
Enter fullscreen mode Exit fullscreen mode

So as you can see copy() is not deep, which means that parameters that are referring to instances will only copy their references. So basically, it's a syntactic sugar over creating new instance via it's constructor and passing the parameters of original instnace.

But that's okay, since user is an immutable data class and it's properties cannot be changed. It would only be problematic if User would have properties defined as var. Then you would be able to do something like this:

...
val system2 = system1.copy()
system1.user.age = 13     // not possible if user is defined as `val`

println(system2.user.age) // prints "13"
Enter fullscreen mode Exit fullscreen mode

The bottom line is, if you keep using val definitions consistently you should be safe with immutability!

Top comments (2)

Collapse
 
davidebarbieri93 profile image
davidebarbieri93

Thanks, very usefull. I was struggling with immutability and basic operations like changin property values.

Collapse
 
darmen profile image
Darmen Amanbayev

Thanks!