解析Kotlin中的委托(包括类委托,属性委托)【笔记摘要】

发布于:2024-07-03 ⋅ 阅读:(21) ⋅ 点赞:(0)

1.委托模式

委托模式:操作对象不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。
例如我们要设计一个自定义类的来实现Set,可以将该实现委托给另一个对象:

class MySet<T> (val helperSet: HashSet<T>) : Set<T> {
    override val size: Int
        get() = helperSet.size

    override fun isEmpty(): Boolean {
        return helperSet.isEmpty()
    }

    override fun iterator(): Iterator<T> {
        return helperSet.iterator()
    }

    override fun containsAll(elements: Collection<T>) = helperSet.containsAll(elements)
    override fun contains(element: T) = helperSet.contains(element)
    
        override fun contains(element: T) = false  //自己重写该方法来定制功能
        fun eat() = println("I can eat.")  //新增方法
}

这样就可以让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,那么MySet就会成为一个全新的数据结构类

2.类委托

如上委托模式的代码实现存在一个弊端,如果接口中待实现的方法特别多,那么相应每个都去调用辅助对象中的相应方法实现将会特别复杂
这时候在Kotlin中使用by关键字,再接上受委托的辅助对象,就可以免去之前所写的一大堆模板式的代码了:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
    override fun contains(element: T) = false  //自己重写该方法来定制功能
    fun eat() = println("I can eat.")  //新增方法
}

类委托本质:简化实现委托模式所需要写的很多不必要代码

3.类可以委托给对象的情况

可以的情况:

 a.构造函数中传入
class MyList2(theList: ArrayList<String>) : List<String> by theList {
}
 b.object的属性
class MyList3 : List<String> by theList {
    object theList : ArrayList<String>()
}
 c.伴生对象中的属性
class MyList4 : List<String> by theList {
    companion object {
        val theList: List<String> = ArrayList<String>()
    }
}
 d.直接创建对象
class MyList6 : List<String> by ArrayList() {
}
 e.调用函数获得对象
fun getDdd() : LinkedList<String> {
    return LinkedList<String>()
}    

class MyList7 : List<String> by getDdd() {
}


不可以的情况:

 a.字段
class MyList : List<String> by theList {  //编译报错,提示:Unresolved reference: theList
    val theList: List<String> = ArrayList<String>()
}
 b.构造函数中的变量
class MyList8 : List<String> by theList {   //编译报错,提示:Unresolved reference: theList
    init {
        val theList = ArrayList<String>()
    }
}

4.属性委托

属性委托的核心思想是将一个属性(字段)的具体实现委托给另一个对象去完成
可以看到,这里使用by关键字连接了左边的p属性和右边的Delegate类去完成。当调用p属性的时候会自动调用Delegate的getValue()方法,当给p属性赋值的时候会自动调用Delegate类的setValue()方法

class MyClass {
    var p by Delegate()
}

Delegate的具体实现:在Delegate类中我们必须实现getValue()和setValue()这两个方法,并且都要使用operator关键字进行声明

class Delegate {
    var proValue: Any? = null

    //第一个参数用于声明该Delegate类的委托功能可以在什么类中使用
    //第二个参数是Kotlin中的一个属性操作类,可用于扩区各种属性相关的值,在当前场景用不着,但必须在方法参数上进行声明
    //返回值可以声明成【任何类型】,根据具体的实现逻辑去写就可以了
    operator fun getValue(myClass: MyClass, prop: KProperty<*>) : Any? {
        return proValue
    }

    //前两个参数和getValue()相同
    //最后一个参数表示具体要赋值给委托属性的值,这个参数类型必须要和getValue()方法返回值的类型保持一致
    operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
        proValue = value
    }
}

现在当我们给p属性赋值时,就会调用Delegate的setValue()方法;当获取p属性的值时,就会调用Delegate类的getValue()方法
如果p属性是使用val关键字声明的,那么就不用在Delegate类中实现setValue()方法,因为它是常量


参考文章:
类声明的右边也能写 by?Kotlin 的接口委托是这么用的