Kotlin'de, shallow copy (yüzeysel kopyalama) yeni bir nesne oluşturur, ancak iç içe nesneler (örneğin, koleksiyon elemanları veya referans alanları) kopyalanmaz, aynı nesneye işaret ederler ki bu da orijinal örnekle aynıdır. Deep copy (derin kopyalama), tüm iç nesnelerin (nesne grafiği) özyinelemeli olarak kopyalanması anlamına gelir, böylece orijinal ve kopya tamamen bağımsız olur.
Kotlin standart kütüphanesinde derin kopyalama için evrensel bir araç yoktur ve manuel olarak uygulanması gerekir, özellikle karmaşık yapılar veya döngü içeren graf yapıları için.
data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moskova", "Londra")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, aynı referans
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, yeni liste kopyası
Eğer iç içe koleksiyonlara sahip bir data class için
.copy()kullanılırsa, koleksiyonlar kopyalanacak mı? Neden?
Hayır, copy() yüzeysel bir kopya oluşturur. İç içe koleksiyonlar ve nesneler aynı nesnelere işaret etmeye devam eder. Bu nesnelerdeki herhangi bir değişiklik hem orijinalde hem de kopyada görülecektir, bu da hatalara yol açabilir.
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] — orijinal de değişti!
Hikaye
Bir projede nesnenin durumunu sıradan
.copy()ile kaydediyorduk, 'anlık görüntü' elde ettiğimizi düşünüp eski duruma döndüğümüzde iç içe liste (mutableList) tüm kopyalarda değişmiş oluyordu çünkü yüzeysel kopyalama yapmıştık.
Hikaye
Belge sisteminde varlıkları Gson ile serileştirme/demirleme yoluyla kopyalayarak derin kopyalama bekliyorduk, ancak nullable alanlar ve tarihler yanlış serileştirildi — sonuç olarak hatalı değerler ve iş mantığı hataları ortaya çıktı.
Hikaye
Android geliştirme için Redux benzeri bir depoya durum geçişi yaparken,
copy()kullanarak durum ağaçları için tam bağımsızlık bekleyerek yapıyorduk. Sonuçta, bir yaklaşım depodaki başka bir durumun değişmesine neden olabiliyordu çünkü aynı iç mutable koleksiyonlarına referans veriyordu.