前言
委托是一种设计模式,基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理
简单的委托模式的例子如下,实现Set接口,实现一个自己的实现类,里面借助HashSet这个辅助对象来实现,如下代码:
class MySet<T>(val helpSet: Set<T>) : Set<T> {
override val size: Int
get() = helpSet.size
override fun contains(element: T): Boolean {
return helpSet.contains(element)
}
override fun containsAll(elements: Collection<T>): Boolean {
return helpSet.containsAll(elements)
}
override fun isEmpty(): Boolean {
return helpSet.isEmpty()
}
override fun iterator(): Iterator<T> {
return helpSet.iterator()
}
}
为啥既然调用的都是辅助对象的方法,不干脆直接使用辅助对象?因为如果我们只是让大部分的方法实现调用辅助对象的方法,少部分的方法由自己重写实现,那么这就体现了委托模式的精妙之处
在Kotlin中,支持委托功能,分为两种:类委托和委托属性
类委托
核心思想:将一个类的具体实现委托给另一个类去完成
Kotlin中的委托使用的关键字是by,只需要在接口声明后面使用by,再接上受委托的辅助对象即可
class MySet1<T>(val helpSet: Set<T>) : Set<T> by helpSet {
fun hello() = print("hello")
}
委托属性
核心思想:将一个属性(字段)的具体实现委托给另一个类去实现
委托属性的语法结构,标准模板如下:
class MyClass {
var temp by Delegate()
}
class Delegate {
var propValue: Any? = null
operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
return propValue
}
operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
propValue = value
}
}
上述写法代表着将temp属性的具体实现委托给Delegate类去完成,当调用temp属性的时候会自动调用Delegate类的getValue()方法,当给temp属性赋值的时候会调用Delegate类的setValue()方法
在Delegate类中必须实现getValue()和setValue()这两个方法,并且要使用operator关键字进行声明
关于Delegate类中的写法,拿getValue()方法来进行说明:第一个参数用于声明该Delegate类的委托功能可以在什么类中使用;第二个参数KProperty<*>是Kotlin中的一个属性操作类,可用于获取各种属性相关的值,当前场景用不到,<*>这种泛型的写法表示是不关心的具体类型,只是为了通过编译而已,类似Java中的<?>写法
有种特殊的场景,当temp属性被定义成常量val修饰时,是不需要实现setValue()方法的,意味着该属性无法修改其值:
class MyClass {
val temp2 by Delegate2()
}
class Delegate2 {
var propValue: Any? = null
operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
return propValue
}
}
lazy函数
可以把需要延迟的函数放在by lazy 代码块中,这块代码在一开始的时候是不回执行的,只有当调用的时候才会执行,实际上,by lazy并不是连在一起的关键字,只有by才是Kotlin的关键字,lazy只是一个高阶函数而已
在lazy函数中会创建并返回一个Delegate对象,当调用该对象时,其实调用的是Delegate对象的getValue()方法,然后getValue()方法中又会调用lazy函数传入Lambda表达式
接下里手动实现一个lazy函数,创建个Later.kt文件,编写代码如下:
class Later<T>(val block: () -> T) {
var value: Any? = null
operator fun getValue(any: Any?, prop: KProperty<*>): T {
if (null == value) {
value = block
}
return value as T
}
}
为了方便使用,使其的用法更加类似于lazy函数,在Later.kt文件中再定义一个顶层函数:
// 定义一个顶层方法
fun <T> later(block: () -> T) = Later<T>(block)
当使用Later时:
class MyClass {
val temp3 by later {
}
}
注意:
在正式项目中,要使用懒加载函数的话,可以使用Kotlin内置的lazy函数