文章目录
定义
有时候我们需要办成某件事但又不想自己做时,可以将其交给别人做,这其实就是一种委托。Kotlin 中可以使用by
关键字声明委托。
继承/实现的委托
某个类ClassA
想要实现一个接口MyInterface
,但是发现接口中的抽象方法非常多,全部实现非常困难。巧的是我们已经有另一个类ClassB
(或者该接口的一个对象)实现了该接口,此时我们可以将ClassA
的实现MyInterface
的工作委托给ClassB
kt
interface MyInterface {
// 超多抽象方法
fun example()
}
class ClassB: MyInterface {
// 超多抽象方法的实现
override fun example() = print("ClassB 实现")
}
class ClassA(classB: ClassB): MyInterface by classB {
// ClassA 不需要自己实现 MyInterface 的方法
}
fun main() {
val classB = ClassB()
ClassA(classB).example()
}
ClassB 实现
如果我们发现ClassB
中的某个方法的实现并不是ClassA
想要的,还可以在ClassA
中声明对MyInterface
方法的重写,此时如果调用ClassA
的example
则会调用新重写的这个:
kt
interface MyInterface {
// 超多抽象方法
fun example()
}
class ClassB: MyInterface {
// 超多抽象方法的实现
override fun example() = print("ClassB 实现")
}
class ClassA(classB: ClassB): MyInterface by classB {
override fun example() = print("ClassA 实现")
}
fun main() {
val classB = ClassB()
ClassA(classB).example()
}
ClassA 实现
属性的委托
委托给对象
我们可以将属性 getter 和 setter 委托给某一个对象,此时会将get
和set
函数委托给类的getValue
和setValue
声明。
val
修饰的不可变变量只有 getter,因此只会将get
委托给getValue
。
最常见的是Lazy
,我们可以使用函数lazy
(后边的 lambda 需要返回一个值作为变量的初始值,该值的类型会作为变量类型)生成一个Lazy
对象,并将某一个变量委托给它:
kt
val name = "Kotlin".also { println("name 初始化") }
val lazyName by lazy { println("lazyName 初始化"); "Kotlin" }
val version = 2.also { println("version 初始化") }
val lazyVersion by lazy { println("lazyVersion 初始化"); 2 }
fun main() {
// 在运行时,name 就被初始化了
// 而委托给 Lazy 的 lazyName 没有被初始化
// 在运行时,version 就被初始化了
// 只有在访问值时,lazyVersion 才会被初始化
lazyVersion
}
name 初始化
version 初始化
lazyVersion 初始化
委托给另一个变量
此时被 委托变量前需要加双冒号::
,事实上,::name
会返回一个KMutableProperty0<String>
对象,所以这是还是将变量委托给了对象。比较特殊的是,该对象并没有声明getValue
或setValue
,委托将交给其 getter 和 setter。
二者的 getter 和 setter 实际上已经绑定在一起了,当其中一个的值改变,另一个也会跟着变。
kt
var name: String = "K1"
fun main() {
var delegateName by ::name
// 打印 name 并改变 delegateName 也是同样的结果
println(delegateName)
name = "K2"
print(delegateName)
}
K1
K2
不 能将可变变量var
委托给不可变变量val
,因为val
没有 setter。
kt
val name: String = "K1"
fun main() {
// 这是错误的
// var delegateName by ::name
}
自定义可委托类
得益于 IDEA 自动补全,我们可以直接写出委托关系var value by MyClass()
,鼠标悬停(或者光标置于标红处,按键盘Alt
+Enter
),点击创建getValue
、setValue
,并加以修改即可。其中,参数名可以自定义。
生成的nothing
(第1个参数)一般是叫thisRef
,它的类型是该变量所有者的类型(例如某个类的成员变量,其所有者是该类)。
这里Nothing?
则表示没有所有者。对于其他类型,如果加了?
,则所有变量都可以委托,如果不加,则只有变量的所有者为指定类型可以委托。
property
中则包含了委托变量的属性,例如property.name
可以获取到变量名。其类型必须为KProperty<*>
。
getValue
的返回值一般需要指明为所要获取值的类型,这里把MyClass.value
返回。
setValue
传入的第3个参数则是要赋的值,这里把值给MyClass.value
kt
import kotlin.reflect.KProperty
class MyClass {
private var value = "MyClass"
operator fun getValue(thisRef: Nothing?, property: KProperty<*>): String {
println("getValue")
return value
}
operator fun setValue(thisRef: Nothing?, property: KProperty<*>, s: String) {
println("setValue")
value = s
}
}
fun main() {
var value by MyClass()
println(value)
value = "Hello"
print(value)
}
getValue
MyClass
setValue
getValue
Hello