Kotlin-20.代理/委托属性(delegated properties)

官方文档: http://kotlinlang.org/docs/reference/delegated-properties.html

1.委托属性

一些常见的属性类型:
    懒加载属性(lazy properties): 只在首次访问时计算属性值;
    可观察属性(observable properties): 监听器可以收到此属性变化的通知;
    一个映射(map)存储多个属性,而不是一个属性对应一个字段!

为涵盖这些情况,Kotlin支持委托属性(delegated properties):
    import kotlin.reflect.KProperty //导入库
    class Example {
    //by Delegate()表示: 属性p的get()和set()会被委托给Delegate的getValue()和setValue()
        var p: String by Delegate()   
    }
    class Delegate() {
        // 取代Example的属性p的get()
        operator fun getValue(t: Any?, prop: KProperty<*>): String {
            // 第一个参数t: 被代理的对象(本例中Example对象)
            // 第二参数prop: 被代理的对象的属性(本例中Example对象的属性p)
            return "$t, ${prop.name}"
        }

        // 取代Example的属性p的set()
        operator fun setValue(t: Any?, prop: KProperty<*>, value: String) {
            // 第三个参数: 被代理的对象的属性值(本例中Example对象的属性p的值)
            println("$t, ${prop.name}, $value")
        }
    }
    fun main(args: Array<String>) {
        val e = Example()
        println(e.p) //调用get方法 输出 Example@6267c3bb, p
        e.p = "MyVal"  //调用set方法 输出 Example@6267c3bb, p, MyVal
    }

2.懒加载属性(lazy properties)

by lazy({ })是接受lambda并返回一个Lazy<T>对象的函数,
返回对象可作为委托,实现了懒加载属性(lazy properties):   
    val a: String by lazy {
        //第一次调用会执行该lamda表达式,并记录结果 
        //后续调用只返回已记录的结果
        println("******")
        "Hello"
        "返回结果" //lamda表达式最后一行作为lazy函数返回值,记录结果
    }
    fun main(args: Array<String>) {
        println(a)
        println(a)
    }
    输出:
    ******
    "返回结果"
    "返回结果"

默认情况下,lazy{}是同步锁住(synchronized): 只能有一个线程执行,其它线程必须等待!
如果需要多个线程同时执行lazy{},那么传递参数LazyThreadSafetyMode.PUBLICATION给lazy()函数
如果确定lazy{}总是单个线程,那么传递参数LazyThreadSafetyMode.NONE,不保证线程安全!
    val a: String by lazy(LazyThreadSafetyMode.NONE){            
        ...
    }
    val a: String by lazy(LazyThreadSafetyMode.PUBLICATION){           
        ...
    }

自kotlin 1.1起,可将局部变量声明为委托属性,例如lazy懒加载属性:
    fun example(computeFoo: () -> Foo) {
        val memoizedFoo by lazy(computeFoo)

        if (someCondition && memoizedFoo.isValid()) {
            memoizedFoo.doSomething()
        }
    }

3.可观察属性(observable properties)

Delegates.observable()有两个参数: 初始值、属性变化时的回调(handler)    
    import kotlin.properties.Delegates //导入库
    class User {
         //如果要拦截(veto否决)赋值,就用vetoable()取代observable()!
        var name: String by Delegates.observable("initName") {
         //回调有三个参数:被观察的属性、属性旧值和属性新值
            prop, old, new -> println("$old ---> $new")
        }
    }
    fun main(args: Array<String>) {
        val user = User()
        user.name = "name111"
        user.name = "name222"
    }
    输出:
    initName ---> name111
    name111 ---> name222

4.一个映射(map)存储多个属性(Storing Properties in a Map)

一个映射(map)存储多个属性,这时可用映射实例对象作为委托,实现委托属性!
    class User(val map: Map<String, Any?>) {
        val name: String by map
        val age: Int     by map
    }
    //1.使用map代理多个属性
    val user = User(mapOf(
        "name" to "lioil",
        "age"  to 24
    ))
    //2.从map中读取多个属性值
    println(user.name) // 输出 lioil
    println(user.age)  // 输出 24

    //把只读Map改为可变MutableMap,也适用于var属性
    class MutableUser(val map: MutableMap<String, Any?>) {
        var name: String by map
        var age: Int     by map
    }

简书:http://www.jianshu.com/p/2464a5106f12 CSDN博客: http://blog.csdn.net/qq_32115439/article/details/73751666
GitHub博客:http://lioil.win/2017/06/26/Kotlin-delegated-properties.html
Coding博客:http://c.lioil.win/2017/06/26/Kotlin-delegated-properties.html