android Kotlin 委托

前言

kotlin 作为google官方android 开发语言,大势所趋,据传到2018底kotlin要全面替代java在android中的地位,其实这个也不担心毕竟kotin和java可以100%互操作。两种语言进行编写也是可以的。

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,
接受请求的对象将请求委托给另一个对象来处理。Kotlin 直接支持委托模式,更加优雅,
简洁。Kotlin 通过关键字 by 实现委托。

类委托

背景 xiaoming,xiaomingfather,xiaomingmother小明,向他爸要钱
买玩具,其实钱是他妈管,找他爸要钱的小明,最后出自他妈手中。(呵呵~~)

package KotlinAgency.Kagency

/**
 * Created by weichyang on 2017/11/2.
 * 委托方
 */
interface KXiaomingFather {
    fun payMoney()
}

p

ackage KotlinAgency.Kagency

import KotlinAgency.XiaomingFather

/**
 * Created by weichyang on 2017/11/2
 *被委托方
 */
class KxiaomingMonther(var payMoney: Int) : XiaomingFather {

    override fun payMoney() {
        print("给小明零花钱" + payMoney + "元")
    }
}
package KotlinAgency.Kagency

import KotlinAgency.XiaomingFather

/**
 * Created by weichyang on 2017/11/2.
 * 在 KXiaoming 声明中,by 子句表示,将 b 保存在 KXiaoming 的对象实例内部,
 * 而且编译器将会生成继承自 XiaomingFather 接口的所有方法, 并将调用转发给 b。
 *
 * 也就是说,编译器会生成 payMoney()方法,拿到Kxiaoming 对象后可以直接调用b中方法,也就是
 * KxiaomingFather中的方法
 *
 */
class KXiaoming(b: XiaomingFather) : XiaomingFather by b {

    fun buyToy(): KXiaoming {
        println("想要买买玩具")
        return this
    }
}
package KotlinAgency.Kagency

import KotlinAgency.XiaomingMother

/**
 * Created by weichyang on 2017/11/2.
 */
object Java2KotlinAnency {

    @JvmStatic
    fun main(args: Array<String>) {
        val angency = XiaomingMother(10)
        KXiaoming(angency).buyToy().payMoney()
    }


}

上面代码就是java中代理写法的Kontlin代码。只是写法变了。

属性委托

属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
属性委托需要提供getValue()setValue()方法的实现。Val只需要提供getValue()方法的实现

属性委托语法格式:

val/var <属性名>: <类型> by <表达式>

var/val:属性类型(可变/只读)
属性名:属性名称
类型:属性的数据类型
表达式:委托代理类

by 关键字之后的表达式就是委托, 属性的 get() 方法(以及set() 方法)将被委托给这个对象的 getValue() 和 setValue() 方法。
属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。

定义一个被委托的类

该类需要包含 getValue() 方法和 setValue() 方法,且参数 thisRef 为进行委托的类的对象,prop 为进行委托的属性的对象。

import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
    var p: String by Delegate()
}
// 委托的类
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, 这里委托了 ${property.name} 属性"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$thisRef 的 ${property.name} 属性赋值为 $value")
    }
}
fun main(args: Array<String>) {
    val e = Example()
    println(e.p)     // 访问该属性,调用 getValue() 函数

    e.p = "Runoob"   // 调用 setValue() 函数
    println(e.p)
}

输出结果为:

Example@433c675d, 这里委托了 p 属性
Example@433c675d 的 p 属性赋值为 Runoob
Example@433c675d, 这里委托了 p 属性

标准委托

Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。

延迟属性 Lazy

lazy() 是一个函数, 接受一个 Lambda 表达式作为参数, 返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

val lazyValue: String by lazy {
    println("computed!")     // 第一次调用输出,第二次调用不执行
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)   // 第一次执行,执行两次输出表达式
    println(lazyValue)   // 第二次执行,只输出返回值
}

执行输出结果:

computed!
Hello
Hello

可观察属性 Observable

顾名思义,可观察属性,就是指属性的变化可以通过函数进行输出

Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。
在属性赋值后会执行事件的响应器(handler),它有三个参数:被赋值的属性、旧值和新值:
以下响应器实现通过lambda函数来实现。

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("初始值") {
        prop, old, new ->
        println("旧值:$old -> 新值:$new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "第一次赋值"
    user.name = "第二次赋值"
}

执行输出结果:

旧值:初始值 -> 新值:第一次赋值
旧值:第一次赋值 -> 新值:第二次赋值

把属性储存在映射中

一个常见的用例是在一个映射(map)里存储属性的值。
这经常出现在像解析 JSON 或者做其他”动态”事情的应用中。
在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。
类似java中的Map。 注:val,var 映射方式不同,一个采用Map,一个采用mutiableMap

class Site(val map: Map<String, Any?>) {
    val name: String by map
    val url: String  by map
}

fun main(args: Array<String>) {
    // 构造函数接受一个映射参数
    val site = Site(mapOf(
        "name" to "菜鸟教程",
        "url"  to "www.runoob.com"
    ))

    // 读取映射值
    println(site.name)
    println(site.url)
}

Not Null

notNull 适用于那些无法在初始化阶段就确定属性值的场合。

class Foo {
    var notNullBar: String by Delegates.notNull<String>()
}

foo.notNullBar = "bar"
println(foo.notNullBar)

局部委托属性

你可以将局部变量声明为委托属性。 例如,你可以使一个局部变量惰性初始化:

fun example(computeFoo: () -> Foo) {
    val memoizedFoo by lazy(computeFoo)

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

memoizedFoo 变量只会在第一次访问时计算。 如果 someCondition 失败,那么该变量根本不会计算。

属性委托要求

对于只读属性(也就是说val属性), 它的委托必须提供一个名为getValue()的函数。
该函数接受以下参数:

thisRef —— 必须与属性所有者类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型
property —— 必须是类型 KProperty<*> 或其超类型
这个函数必须返回与属性相同的类型(或其子类型)。
对于一个值可变(mutable)属性(也就是说,var 属性),除 getValue()函数之外,它的委托还必须 另外再提供一个名为setValue()的函数, 这个函数接受以下参数:

property —— 必须是类型 KProperty<*> 或其超类型
new value —— 必须和属性同类型或者是它的超类型。

翻译规则

在每个委托属性的实现的背后,Kotlin 编译器都会生成辅助属性并委托给它。 例如,对于属性 prop,生成隐藏属性 prop$delegate,而访问器的代码只是简单地委托给这个附加属性:

class C {
    var prop: Type by MyDelegate()
}

// 这段是由编译器生成的相应代码:
class C {
    private val prop$delegate = MyDelegate()
    var prop: Type
        get() = prop$delegate.getValue(this, this::prop)
        set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}

Kotlin 编译器在参数中提供了关于 prop 的所有必要信息:第一个参数 this 引用到外部类 C 的实例而 this::prop 是KProperty 类型的反射对象,该对象描述 prop 自身。

提供委托

通过定义 provideDelegate 操作符,可以扩展创建属性实现所委托对象的逻辑。
如果 by 右侧所使用的对象将 provideDelegate 定义为成员或扩展函数,那么会调用该函数来 创建属性委托实例。
provideDelegate 的一个可能的使用场景是在创建属性时(而不仅在其 getter 或 setter 中)检查属性一致性。

import kotlin.reflect.KProperty

/**
 * 就是给A和委托B之间插入一个中间件而已
 * 按照正常属性委托流程,需要自己根据val ,var实现getValue(),或者setValue(),提供
 *委托实在属性委托的基础上增加一个 provideDelegate()成员函数或者扩展函数作为中间件,进行一些操作
 *来实现,感觉有点类似,面向切面编程的切面效果。
 */

class dge<T>(t: T) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, 这里委托了 ${property.name} 属性"
    }
}

class ResourceLoader<T>(id: Int) {

    //可以扩展创建属性实现所委托对象的逻辑
    operator fun provideDelegate(
            thisRef: MyUI,
            prop: KProperty<*>): dge<T?> {
        checkProperty(thisRef, prop.name)
        // 创建委托
        var t: T? = null
        return dge(t)
    }

    private fun checkProperty(thisRef: MyUI, name: String) {
        println(name)
    }
}

fun <T> bindResource(id: Int): ResourceLoader<T> {
    return ResourceLoader<T>(id)
}

class MyUI {
    //被委托类.委托类,委托
    //将 image 和 text的 get,set属性 委托,委托类个getValue( )
    val image by ResourceLoader<Int>(1) //两个属性进行了委托、实现getValue()
    val text by ResourceLoader<Int>(2)
}

fun main(args: Array<String>) {
    val myui: MyUI = MyUI()
    println(myui.image)
}

引用
http://www.runoob.com/kotlin/kotlin-delegated.html

猜你喜欢

转载自blog.csdn.net/o279642707/article/details/78435585