如何在Kotlin中为自定义包装类实现赋值运算符重载?
实现Kotlin包装类的简化赋值操作
现有代码结构
我们有一个包装字符串的可变类:
class StringWrapper( var value: String = "" ) { override fun toString(): String = value }
将其作为数据持有者类的属性使用:
class DataHolder { val name = StringWrapper() override fun toString(): String = "Data(name=$name)" }
当前赋值的痛点
目前操作包装类内容需要冗余的.value调用,直接赋值字符串或其他包装类实例会报错:
val dataAlpha = DataHolder() // 冗余写法 dataAlpha.name.value = "alpha" // 直接赋值字符串会类型不匹配 dataAlpha.name = "alpha" // ERROR Type mismatch. // Required: StringWrapper // Found: String
实例间复制内容也需要手动访问.value,直接赋值实例会因为属性是val报错:
val dataAlpha = DataHolder() dataAlpha.name.value = "alpha" val dataAlphaCopy = DataHolder() // 直接赋值实例报错 dataAlphaCopy.name = dataAlpha.name // ERROR Val cannot be reassigned
解决方案:重载setValue扩展运算符
通过给数据持有者类重载setValue扩展函数,可以实现简化赋值的效果——让编译器将data.name = x转换为修改包装类内部的value属性,而非重新赋值name本身。
针对单个类的实现
给DataHolder添加两个setValue重载,分别支持字符串赋值和StringWrapper实例赋值:
import kotlin.reflect.KProperty // 支持直接赋值字符串 operator fun DataHolder.setValue( thisRef: Any?, property: KProperty<*>, value: String ) { if (property.name == "name") { this.name.value = value } } // 支持直接赋值StringWrapper实例(复制内部值) operator fun DataHolder.setValue( thisRef: Any?, property: KProperty<*>, value: StringWrapper ) { if (property.name == "name") { this.name.value = value.value } }
添加后即可使用简化写法:
val dataAlpha = DataHolder() dataAlpha.name = "alpha" // 等价于dataAlpha.name.value = "alpha" val dataAlphaCopy = DataHolder() dataAlphaCopy.name = dataAlpha.name // 等价于dataAlphaCopy.name.value = dataAlpha.name.value
通用化实现
如果需要让所有包含StringWrapper类型val属性的类都支持该特性,可以编写通用扩展:
import kotlin.reflect.KProperty // 通用支持字符串赋值 operator fun <T : Any> T.setValue( thisRef: Any?, property: KProperty<*>, value: String ) { val wrapperProperty = this::class.memberProperties .find { it.name == property.name && it.returnType.classifier == StringWrapper::class } as? KProperty1<T, StringWrapper> wrapperProperty?.get(this)?.value = value } // 通用支持StringWrapper实例赋值 operator fun <T : Any> T.setValue( thisRef: Any?, property: KProperty<*>, value: StringWrapper ) { val wrapperProperty = this::class.memberProperties .find { it.name == property.name && it.returnType.classifier == StringWrapper::class } as? KProperty1<T, StringWrapper> wrapperProperty?.get(this)?.value = value.value }
说明
这种方式利用了Kotlin的属性运算符重载机制,虽然name是不可变的val属性,但编译器会将赋值语法转换为调用我们定义的setValue扩展函数,从而修改包装类内部的可变值,和Gradle Kotlin DSL中使用的实验特性原理一致。
内容的提问来源于stack exchange,提问作者aSemy
相关产品推荐
相关产品推荐

