我不是罗大锤我不是罗大锤

我不是罗大锤

我不是罗大锤我不是罗大锤

我不是罗大锤

首页首页
分类分类
标签标签
友情链接友情
日记日记
开发中
博客仍在开发中。
Powered byNola
Kotlin:类委托和委托属性Kotlin:类委托和委托属性

Kotlin:类委托和委托属性

&Kotlin

允许评论

4 年前

委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。

一、类委托

类委托的核心思想在于将一个类的具体实现委托给另一个类去完成。在前面的章节中,我们曾经使用过 Set 这种数据结构,它和 List 有点类似,只是它所存储的数据是无序的,并且不能存储重复的数据。Set 是一个接口,如果要使用它的话,需要使用它的具体的实现类,比如 HashSet。而借助于委托模式,我们可以轻松实现一个自己的实现类。比如定义一个 MySet,并让它实现 Set 接口,代码如下所示:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> {
    override val size: Int
        get() = helperSet.size
    override fun contains(element: T) = helperSet.contains(element)
    override fun containsAll(elements: Collection<T>) = 
            helperSet.containsAll(elements)
    override fun isEmpty() = helperSet.isEmpty()
    override fun iterator() = helperSet.iterator()
}

可以看到,MySet 构造函数中接收了一个 HashSet 参数,相当于一个辅助对象,然后在 Set 接口所有的方法实现中,我们都没有进行自己的实现,而是调用了辅助对象中相应的方法实现,这其实就是一种委托模式。

我们如果让大部分的方法实现调用辅助对象中的方法,少量部分的方法实现由自己来重写,甚至加入一些自己独有的方法, 那么 MySet 就会成为一个全新的数据结构类,这就是委托模式的意义所在。

但是目前这种写法也有一定弊端,如果接口中待实现方法有几十个甚至上百个,每个都去这样调用辅助对象中的方法的话就会非常麻烦,所以我们就需要使用委托的功能来解决。

Kotlin 中委托的关键字是 by,只需要在接口声明后面使用 by 关键字,再加上委托的辅助对象,就可以免去之前所写的一大堆板式的代码了。如下所示:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
    // 这段代码和上段代码实现的效果是一模一样的
    // 如果我们要对某个方法重新实现,只需要单独重写那一个方法即可
    // 下面代码重写了isEmpty方法以及新增helloWorld方法
    fun helloWorld() = println("Hello World")
    override fun isEmpty() = false
}

二、属性委托

委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。下面是委托属性的语法结构:

class MyClass {
    var p by Delegate()
}

这里使用 by 关键字将 p 属性的具体实现委托给了 Delegate 类去完成,当调用 p 属性的时候会自动调用 Delegate 类的 getValue() 方法,当给 p 属性赋值的时候会自动调用 Delegate 类的 setValue() 方法,下面是 Delegate 类的具体实现:

class Delegate {
    var propValue: Any? = null
    operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
        return propValue
    }
    
    operator fun setValue(myClass: MyClass, prop: KProperty<*>, 
            valueL Any?) {
        propValue = value
   }
}

这是一种标准的代码实现模板,在 Delegate 类中我们必须实现 getValue() 和 setValue() 这两个方法,并且都要使用 operator 关键字进行声明。

getValue() 接收两个参数,第一个参数用于声明该 Delegate 类的委托功能可以在什么类中使用,这里写成 MyClass 表示仅可在 MyClass 类中使用,第二个参数 KProperty<*> 是 Kotlin 中的一个属性操作类,另外 <*> 这种写法表示不知道或者不关心泛型的具体类型,只是为了通过语法编译,类似于 Java 中的 <?> 写法,返回值可以声明成任何类型,根据具体实现逻辑写就可以。

三、实现自己的 lazy 函数

by lazy{} 代码块中的代码在一开始并不会执行,只有当变量被首次调用的时候,代码块中的代码才会被执行。实际上 by lazy 并不是连在一起的关键字,只有 by 才是关键字,lazy 只是一个高阶函数而已,在 lazy 函数中会创建并返回一个 Delegate 对象,当我们调用 p 属性的时候,其实调用的是 Delegate 对象的 getValue() 方法,然后 getValue() 方法中又会调用 lazy 函数传入的 Lambda 表达式,这样表达式中的代码就可以得到执行了,并且调用 p 属性后得到的值就是 Lambda表达式最后一行代码的返回值。

class Later<T>(val block: () -> T) {
    val value: Any? = null
    operator fun getValue(any: Any?, prop: KProperty<*>): T {
        if(value == null) {
            value = block()
        }
        return value as T
    }
}

// 为了使它的用法更类似于lazy函数,再定义一个顶层函数
fun <T> later(block: () -> T) = Later(block)

// 替换之前的lazy代码
val uriMatcher by later {
    val matcher = UriMatcher(UriMatcher.NO_MATCH)
    ...
    matcher
}
目录
一、类委托
二、属性委托
三、实现自己的 lazy 函数
暂无评论