В Kotlin, shallow copy (поверхностное копирование) создает новый объект, но вложенные объекты (например, элементы коллекций или поля-ссылки) не копируются, а ссылаются на те же объекты, что и исходный экземпляр. Deep copy (глубокое копирование) означает рекурсивное копирование всех вложенных объектов (графа объектов), чтобы оригинал и копия были полностью независимы.
В стандартной библиотеке Kotlin нет универсального средства для глубокого копирования, и его необходимо реализовывать вручную, особенно для сложных структур или графов с циклами.
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, одна и та же ссылка
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, новая копия списка
Если использовать
.copy()для data class с вложенными коллекциями, будут ли коллекции скопированы? Почему?
Нет, copy() создает поверхностную копию. Вложенные коллекции и объекты останутся ссылками на те же объекты. Любые изменения в этих объектах будут видны и в оригинале, и в копии, что может привести к багам.
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] — модифицировался и оригинал!
История
На проекте сохраняли состояние объекта через обычный
.copy()data class, считая, что получают "снимок". При возврате к старому состоянию оказывалось, что вложенный список (mutableList) был изменен во всех копиях из-за поверхностного копирования.
История
В системе документов копировали сущности через сериализацию/десериализацию с помощью Gson, ожидая глубокое копирование, но nullable поля и даты сериализовались неправильно — в результате появлялись некорректные значения и ошибки бизнес-логики.
История
При миграции состояния в Redux-подобном store для Android-разработки стали использовать
copy()для деревьев состояния, ожидая полную независимость копий. В итоге одна ветка store могла мутировать состояние другой из-за ссылок на одни и те же внутренние mutable-коллекции.