Kotlin Tip #22: Use copy() to Modify Immutable Objects— 100 Kotlin Tips in 100 Days

Raphael De Lio
Kotlin with Raphael De Lio
3 min readMar 2, 2024

Twitter | LinkedIn | YouTube | Instagram
Tip #21: Use Sealed Classes to Improve Error Handling

We've already discussed the benefits of data classes in this tip: . Data classes allow

When defining classes in Kotlin, we have the option of defining their properties as immutable. Having immutable properties enhances the predictability and thread-safety of our code, making it less prone to errors and easier to maintain. (See: )

However, sometimes we have to create a slightly modified copy of an existing object while keeping the original object unchanged. This is where the copy() function comes into play.

Among all the utility functions automatically generated for Kotlin Data Classes (See: ) we can find the copy() function. This function allows for the creation of a new object with some properties modified, based on an existing object.

Consider a data class User, defined as follows:

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

If you want to create a new User instance based on an existing User but with a different age, you can use the copy() function:

val originalUser = User(name = "Raphael De Lio", age = 30)
val updatedUser = originalUser.copy(age = 31)

This makes the task of creating a slightly modified copy of an existing object much easier. Especially when this object has several properties.

The only thing to bear in mind is that the copy() function performs a shallow copy. This means that for properties that hold objects, the copied object will reference the same instances as the original object. Therefore, if you modify the mutable properties of the copied object, those changes will be reflected in the original object as well.

Consider a data class with a mutable property:

data class UserProfile(val name: String, val addresses: MutableList<String>)

Using copy() in this case:

val originalProfile = UserProfile("Raphael De Lio", mutableListOf("123 Main St"))
val copiedProfile = originalProfile.copy()

If you add a new address to copiedProfile.addresses, it will also appear in originalProfile.addresses because both properties reference the same MutableList instance. Let’s try it:

If you run the code above, you will see that the address list from both variables have been cleared.

To avoid unintended side effects, you would need to explicitly copy mutable properties as needed. For example:

val copiedProfile = originalProfile.copy(addresses = originalProfile.addresses.toMutableList())

Let’s try it now:

In this example, copiedProfile has its own independent address list. Changes to the address list in copiedProfile will not affect originalProfile, achieving true immutability.

I hope you have enjoyed this tip! Don’t forget to subscribe and stay tuned for more Kotlin tips!

Stay curious!

Tip #23: Use type aliases to provide alternative names for existing types

Contribute

Writing takes time and effort. I love writing and sharing knowledge, but I also have bills to pay. If you like my work, please, consider donating through Buy Me a Coffee: https://www.buymeacoffee.com/RaphaelDeLio

Or by sending me BitCoin: 1HjG7pmghg3Z8RATH4aiUWr156BGafJ6Zw

Follow Me on Social Media

Stay connected and dive deeper into the world of Kotlin with me! Follow my journey across all major social platforms for exclusive content, tips, and discussions.

Twitter | LinkedIn | YouTube | Instagram

--

--