“Interesting Kotlin” series, through solving problems to deepen their understanding of Kotlin.
0 x0b: Copy
data class Container(val list: MutableList<String>)
fun main(args: Array<String>) {
val list = mutableListOf("one", "two")
val c1 = Container(list)
val c2 = c1.copy()
list += "oops"
println(c2.list.joinToString())
}
Copy the code
What is the result of running the above code? Optional:
- one, two
- one, two, oops
- UnsupportedOperationException
- will not compile
Think about it and write down the answers in your mind.
Analysis of the
Is the copy function generated by the Kotlin compiler for the data class a deep copy or a shallow copy?
- In the case of deep copy, the answer is one, two
- For shallow copy, the answer would be one, two, oops
Deep copy, shallow copy
Deep and shallow copies are only for reference data types such as Object and Array.
- Shadow Clone A shallow copy copies only the application of the object, that is, the pointer to the object, but not the object itself. The old and new objects share the same memory.
- Deep Clone A Deep Clone creates an identical object. The new object does not share memory with the original object. The new object does not change to the original object.
Pictures from: stackoverflow.com/questions/1…
Data Class
In Kotlin, the compiler helps the data class generate the following member functions:
equals()
和hashCode()
toString()
componentN
copy()
However, the componentN and copy() functions are not allowed to be overridden. More importantly, the copy() function does a shallow copy.
So in the question, the list of member variables in the c1 and C2 objects point to the same memory space (the list of variables), so when the list changes, because the member variables in both objects change.
The correct answer is:
Option 2: One, two, oops
extension
Although the data class is designed to help developers hold immutable data so that they can distinguish between data classes and business classes, Kotlin’s lack of strict compilation restrictions, coupled with the shallow copy problem of the copy function, can easily cause problems similar to the one in the title. While developers complain about design flaws, they are also actively looking for solutions.
noCopy
A compiler plug-in that removes the copy method from the data class. Source address: github.com/AhmedMourad… .
Add the dependent
buildscript { repositories { mavenCentral() // Or maven { url "https://plugins.gradle.org/m2/" } } dependencies { Classpath "dev. Ahmedmourad. Nocopy: nocopy - gradle - plugin: 1.4.0"}}Copy the code
Plugins {id "dev. Ahmedmourad. Nocopy. Nocopy - gradle - plugin" version "1.4.0}"Copy the code
use
Functionality is realized through @noCopy annotation
@NoCopy data class User(val name: String, val phoneNumber: String) User("Ahmed", "+201234567890").copy(phoneNumber = "Happy birthday!" ) // Unresolved reference: copyCopy the code
deepCopy
BennyHuo provides you with two deep copy methods: one based on Kotlin reflection and one based on KAPT. Source address: github.com/bennyhuo/Ko…
reflection
- Add the dependent
Implementation (" com. Bennyhuo. Kotlin. Reflect: deepcopy - reflect: 1.5.0 ")Copy the code
- use
data class Speaker(val name: String, val age: Int) data class Talk(val name: String, val speaker: Speaker) class DeepCopyTest { @Test fun test() { val talk = Talk("DataClass in Action", Speaker("Benny Huo", 30)) val newTalk = talk.deepCopy() assert(talk == newTalk) assert(talk ! == newTalk) } }Copy the code
KAPT
- Add the dependent
apply plugin: "kotlin-kapt" ... Dependencies {kapt (" com. Bennyhuo. Kotlin. Apt: deepcopy - compiler: 1.5.0 ") Implementation (" com. Bennyhuo. Kotlin. Apt: deepcopy - runtime: 1.5.0 ")}Copy the code
- add
@DeepCopy
annotations
@DeepCopy
data class Speaker(val name: String, val age: Int)
@DeepCopy
data class Talk(val name: String, val speaker: Speaker)
Copy the code
- use
fun Talk.deepCopy(name: String = this.name, speaker: Speaker = this.speaker): Talk = Talk(name, speaker.deepCopy())
fun Speaker.deepCopy(
name: String = this.name,
age: Int = this.age,
company: Company = this.company
): Speaker = Speaker(name, age, company.deepCopy())
Copy the code
Note: If the member attribute is a data class annotated with @deepCopy, DeepCopy will be called recursively.