标题上写了三个话题, 它们是什么关系呢?by关键字用于属性委托和类委托,而Lazy是属性委托的一种优秀应用。

  • 属性委托

属性的赋值来自定义好的委托类。使用更加简单,只要用关键字by指定委托类,就可以在运行时赋值了。属性委托也是约定的功能之一。

看下面的代码:目的是给MainActivity的ViewBinding根实例赋值。

class MainActivity  { 

    override val viewDataBinding: ActivityMainBinding by ActivityDataBindingDelegate(R.layout.activity_main)
}

 viewDataBinging的赋值来自ActivityDataBindingDelegate代理类getValue()函数的返回结果。

class ActivityDataBindingDelegate<out VD : ViewDataBinding>(@LayoutRes private val layoutRes: Int) :
    ReadOnlyProperty<Activity, VD> {

    private var binding: VD? = null

    override fun getValue(thisRef: Activity, property: KProperty<*>): VD =
        binding ?: DataBindingUtil.setContentView<VD>(thisRef, layoutRes)
            .also { binding = it }

}

而ActivityDataBindingDelegate实现了接口ReadOnlyProperty的getValue, getValue的函数签名是固定格式,而且我们知道约定要有operator修饰。

属性委托也有成对的get/set模式, 也就是接口ReadWriteProperty。

public fun interface ReadOnlyProperty<in T, out V> {
    public operator fun getValue(thisRef: T, property: KProperty<*>): V
}


public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> { 
    public override operator fun getValue(thisRef: T, property: KProperty<*>): V
 
    public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}

当然,不是说一定要实现这两个接口。属性委托限制的只是get/set的定义。

也可以这样:

class TestDelegate {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return  "alan"
    }
    
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("alan"+value)
    }
}

fun main() {
    var testDelegate by TestDelegate()
    // 使用testDelegate ,这时候会去调用TestDelegate的getValue方法
    println(testDelegate )
    // 设置testDelegate 的值,这时候去会调用setValue方法
    demoString = "gong"// 输出:"alan gong"
}
  • Lazy懒加载

它的意思是参数只会初始化一次,第一调用时会返回第一次初始化的值。

而且第一次使用是属性值初始化的时候,不是说在类的其他方法里第一次调用的时候。

class TestLazy() {
    private val name by lazy {
        "alan"
    }
}

lazy是个函数,返回实现Lazy接口的对象实例。默认是线程安全的SynchronizedLazyImpl。

按道理Lazy实现类SynchronizedLazyImpl,应该有属性委托的getValue()函数,但是看源码是没有的,但是在编译时生成的。不知道是为什么。但是有一个get()方法,作用是初始化值,以及处理多线程同步问题。

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // final field is required to enable safe publication of constructed instance
    private val lock = lock ?: this

    override val value: T
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}
  • 类委托

这个就是设计模式中的代理模式。只是比java更简便了。有一点要记住,代理方法都必须是事先定义好的接口。

class ByTest {

    // 定义一个代理接口,和一个代理方法 show(), 这个接口必须定义。
    interface Proxy{
        fun show()
    }

    // 实现类实现 Proxy接口, 并实现 show 方法
    open class ProxyImpl : Proxy{
        override fun show() {
            Log.e("ProxyImpl ::show()")
        }
    }

    // 定义代理类实现 Proxy接口, 构造函数参数是一个 Proxy对象
    // by 后跟 Proxy对象, 不需要再实现 show()
    class NeedProxy(proxy: Proxy) : Proxy by proxy{
        fun showOther() {
            Log.e("NeedProxy::showOther()")
        }

    }

    // main 方法
    fun mainGo() {
        val base = ProxyImpl ()
        NeedProxy(base).show()
        NeedProxy(base).showOther()
    }
}

输出:

 ProxyImpl::show()
 NeedProxy::showOther()