En Kotlin, shallow copy (copia superficial) crea un nuevo objeto, pero los objetos anidados (por ejemplo, elementos de colecciones o campos de referencia) no se copian, sino que hacen referencia a los mismos objetos que el objeto original. Deep copy (copia profunda) significa copiar recursivamente todos los objetos anidados (el gráfico de objetos), de modo que el original y la copia sean completamente independientes.
No hay una herramienta universal en la biblioteca estándar de Kotlin para la copia profunda, y debe implementarse manualmente, especialmente para estructuras complejas o gráficos con ciclos.
data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moscú", "Londres")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, la misma referencia
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, nueva copia de la lista
Si se usa
.copy()para un data class con colecciones anidadas, ¿se copiarán las colecciones? ¿Por qué?
No, copy() crea una copia superficial. Las colecciones anidadas y los objetos seguirán siendo referencias a los mismos objetos. Cualquier cambio en estos objetos será visible tanto en el original como en la copia, lo que puede provocar errores.
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] — ¡se modificó el original!
Historia
En un proyecto, se guardó el estado de un objeto a través del
.copy()normal de un data class, creyendo que obtenían una "instantánea". Al volver a un estado anterior, resultó que la lista anidada (mutableList) había sido modificada en todas las copias debido a la copia superficial.
Historia
En un sistema de documentos, se copiaron entidades a través de la serialización/deserialización usando Gson, esperando una copia profunda, pero los campos anulables y las fechas se serializaban incorrectamente, lo que resultaba en valores incorrectos y errores en la lógica de negocio.
Historia
Al migrar el estado en un store similar a Redux para el desarrollo de Android, comenzaron a usar
copy()para árboles de estado, esperando completa independencia de las copias. Al final, una rama del store podía mutar el estado de otra debido a referencias a las mismas colecciones mutables internas.