ProgrammingKotlin Developer

Explain the difference between shallow copy and deep copy in Kotlin. How to implement deep copying of objects, and what are the typical mistakes when copying complex structures?

Pass interviews with Hintsage AI assistant

Answer.

In Kotlin, shallow copy creates a new object, but nested objects (such as collection elements or reference fields) are not copied, but reference the same objects as the original instance. Deep copy means recursively copying all nested objects (the object graph) so that the original and the copy are completely independent.

There is no universal means for deep copying in the Kotlin standard library, and it needs to be implemented manually, especially for complex structures or graphs with cycles.

Example of shallow copying:

data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moscow", "London")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, the same reference

Example of deep copying:

data class Person(val name: String, val addresses: List<String>) { fun deepCopy(): Person = Person(name, addresses.toList()) } val deep = original.deepCopy() printfn(original.addresses === deep.addresses) // false, new copy of the list

Trick question.

If .copy() is used for a data class with nested collections, will the collections be copied? Why?

Answer with an example:

No, copy() creates a shallow copy. Nested collections and objects will remain references to the same objects. Any changes to these objects will be visible in both the original and the copy, which can lead to bugs.

data class Box(val items: MutableList<String>) val box1 = Box(mutableListOf("A")) val box2 = box1.copy() box2.items.add("B") printfn(box1.items) // [A, B] — original was modified!

Examples of real errors due to lack of knowledge about the nuances of the topic.


Story

In the project, we saved the object's state through a regular .copy() of a data class, believing that we get a "snapshot". When returning to the old state, it turned out that the nested list (mutableList) was changed in all copies due to shallow copying.


Story

In the document system, we copied entities through serialization/deserialization using Gson, expecting deep copying, but nullable fields and dates were serialized incorrectly — as a result, incorrect values and business logic errors appeared.


Story

When migrating state in a Redux-like store for Android development, we started using copy() for state trees, expecting complete independence of copies. As a result, one branch of the store could mutate the state of another due to references to the same internal mutable collections.