kotlin基础十三:by

95 阅读3分钟

前言

Kotlin中比较重要的知识点委托。委托属性和委托类。委托属性是很好的懒加载模式;委托类是替代继承很好的方式。Kotlin中使用by关键字

委托概念

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

class NewList<out T>(private val list:MutableList<T>){
    fun isEmpty()=list.isEmpty()
    fun add(item:@UnsafeVariance T) = list.add(item)
    fun remote(item:@UnsafeVariance T) = list.remove(item)
    fun myMethod(){}
}

在类NewList中定义几个简单方法,并没有让NewList自己去实现方法的逻辑,交给了其构造函数中的属性参数list去处理

委托属性

委托属性语法:val/var <属性名>:<类型>by<表达式> 属性对应get或set会被委托给它的getValue或setValue方法。属性的委托不必实现任何接口,需要提供getValue或setValue

fun test() {
    val a = A()
    println(a.p)
    println(a.q)
}
class Delegate{
    operator fun getValue(thisRef:Any?,prop:KProperty<*>):String{
        return "$thisRef name:${prop.name}"
    }
}
class A{
    val p:String by Delegate()
    val q:String by Delegate()
}
输出:
com.georege.test.Test$A@565f390 name:p
com.georege.test.Test$A@565f390 name:q

创建名为Delegate类,使用operator(操作符重载)修饰方法getValue,thisRef代表被委托的对象实例,prop存储了被委托属性相关信息。访问p属性就相当于调用Delegate中getValue方法

fun test() {
    val a = A()
    println(a)
    val de = Delegate()
    // 对属性a的访问,等价下面调用
    println(de.getValue(a,a::p))
    println(de.getValue(a,a::q))
}
class Delegate{
    operator fun getValue(thisRef:Any?,prop:KProperty<*>):String{
        return "$thisRef name:${prop.name}"
    }
}
class A{
    val p:String by Delegate()
    val q:String by Delegate()
}
输出:
com.georege.test.Test$A@5be1d0a4
com.georege.test.Test$A@5be1d0a4 name:p
com.georege.test.Test$A@5be1d0a4 name:q

a::p获取的是Kproperty的引用,只是委托一个属性get()方法,那么该委托属性声明只能用val类型。只有同时委托get和set才能将委托属性声明为var类型。

fun test() {
    val a = A()
    println(a.p)
    a.p = "by this property"
}
class Delegate{
    operator fun getValue(thisRef:Any?,prop:KProperty<*>):String{
        return "$thisRef name:${prop.name}"
    }
    operator fun setValue(thisRef:Any?,prop:KProperty<*>,value:String){
        println("$thisRef name:${prop.name} value:$value")
    }
}
class A{
    var p:String by Delegate()
}
输出:
com.georege.test.Test$A@415b0b49 name:p
com.georege.test.Test$A@415b0b49 name:p value:by this property

被委托的属性p赋值调用了Delegate类中setValue方法。在A类中将被委托属性p声明了var类型。

委托属性真相

calss A{
    var p:String by Delegate()
}

反编译成java后

class A{
    private val prop$delegate = Delegate()
    var p:String
        get()=prop$delegate.getValue(this::prop)
        set(value:String)=prop$delegate.setValue(this,this::prop,value)
}

上面就是kotlin编译器生成的属性委托实现原理

lazy函数

Kotlin中高阶函数lazy()

val property:String by lazy{"test"}

lazy函数拥有一个函数类型的参数,调用lazy函数使用了Lambda,Lambda是函数类型的实例。lazy函数中的函数类型的参数拥有一个返回值,该返回值的类型决定了被委托属性的类型,lazy函数的Lambda设置任意类型作为委托属性的值,推断出lazy函数是一个泛型函数

fun <T> lazy(block:()->T){}

lazy函数需要返回一个委托类的对象,委托类的对象重载get返回值在lazy函数中设置的类型

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

lazy函数需要完成属性的委托,函数本身返回值需要委托类

fun <T> lazy(block:()->):Later<T>{
    return Later(block)
}

简单测试运行:

fun test() {
    val name:String by lazy { "by name" }
    println(name)
}
class Later<T>(private val block:()->T) {
    private var value: Any? = null
    operator fun getValue(thisRef: Any?, prop: KProperty<*>): T {
        if (value == null) {
            value = block()
        }
        return value as T
    }
}
fun <T> lazy(block:()->T):Later<T>{
    return Later(block)
}
输出:
by name

API中的lazy函数要更复杂,提供三种不同使用模式。

委托类

委托模式是实现继承很好的替代方式

class MyList<E>(private val list:MutableList<E>):MutableList<E> by list{
    override val size: Int
        get() = list.size
}

定义一个MyList泛型,实现MutableList接口。定义类型为MutableList名为list的属性,在继承类后加by list。list将会在MyList内部存储,编译器生成MutableList中所有的方法转发给list。MyList反编译成Java

public final calss MyList implements List,KMutableList{
    private final List list;
    public int getSize(){
        return list.size();
    }
    public final int size(){
        retrun this.getSize();
    }
    public MyList(@NotNull List list){
        Intrinsics.checkNotNullParameter(list,"list");
        super();
        this.list = list;
    }
    ....
}

实际开发中,当MutableList不能满足我们现有的需求可以用委托实现,在MyList中新增自定义方法,让MyList成为新的数据结构,使用委托类的优势。

总结

属性委托和类委托,我们要运用到实际开发中,掌握语法真实含义。