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())
以上代码,运行结果是什么?可选项:
one, two
one, two, oops
UnsupportedOperationException
will not compile
思考一下,记录下你心中的答案。
Kotlin 编译器帮 data class
生成的 copy
函数是深拷贝还是浅拷贝?
若是深拷贝,答案就是 one, two
若是浅拷贝,答案就是 one, two, oops
深拷贝、浅拷贝
深拷贝和浅拷贝只针对Object
和Array
这样的引用数据类型。
浅拷贝(Shadow Clone) 浅拷贝只复制对象应用,即指向对象的指针,而不复制对象本身,新旧对象共享同一块内存。
但是,componentN
和 copy()
函数不允许被覆写。更重要的一点,copy()
函数完成的是浅拷贝 。
因此题目中,c1
和 c2
对象中成员变量list
指向同一份内存空间(即变量list
),所以当list
改变时,由于两个对象中的成员变量均会改变。
所以,正确答案为 :
选项 2 :one, two, oops
虽然 data class
的设计初衷是帮助开发者持有一些不可变的数据,以便区分数据类和业务类,但是 Kotlin 在编译上并未做严格的限制,再加上copy
函数浅拷贝的问题,程序上很容易出现与题目中相似的问题。开发者在抱怨设计缺陷的同时,也在积极寻找解决办法。
noCopy
一个从数据类中删除了 copy
方法的编译器插件。源码地址:github.com/AhmedMourad… 。
buildscript {
repositories {
mavenCentral ()
maven { url "https://plugins.gradle.org/m2/" }
dependencies {
classpath " dev.ahmedmourad.nocopy:nocopy-gradle-plugin:1.4 .0 "
plugins {
id "dev.ahmedmourad.nocopy.nocopy-gradle-plugin" version "1.4.0"
通过 @NoCopy
注解实现功能
@NoCopy
data class User (val name: String, val phoneNumber: String)
User("Ahmed" , "+201234567890" ).copy(phoneNumber = "Happy birthday!" )
deepCopy
BennyHuo 老师为你解忧,提供两种深拷贝的方法:一种基于 Kotlin 反射,一种基于 KAPT。源码地址:github.com/bennyhuo/Ko…
implementation ("com.bennyhuo.kotlin.reflect:deepcopy-reflect:1.5 .0 ")
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)
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 ")
添加 @DeepCopy
注解
@DeepCopy
data class Speaker (val name : String, val age : Int)
@DeepCopy
data class Talk (val name : String, val speaker : Speaker)
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())
注意:如果成员属性是使用 @DeepCopy
注释的数据类,程序将会递归调用 deepCopy
。
Kotlin
Android
Android Jetpack
3.9w
wang_zd
Kotlin
Android
1205
Android
Kotlin
Android Jetpack
2.1w
Jetictors
Android
Kotlin
4.0w
Jetictors
Android
Kotlin
2.3w
Jetictors
Android
Kotlin
1.7w
sweetying
Kotlin
Android