手记

带你全面了解kotlin中的委托机制!

在设计模式中有一种模式,叫做委托模式(或是代理模式)。它在维基百科上的解释是这样子的:

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承,它还使我们可以模拟mixin。

这个设计模式符合合成复用原则的思想。少用强耦合的继承关系,尽可能的使用聚合来代替继承。

在Java中并没有在语法层次上对委托模式进行支持。在Java之后出现的C#就对委托模式进行了支持,通过 delegate 进行实现。而现在,同样是运行在 JVM 上的语言 Kotlin 也提供了语法层面的支持。

在Kotlin中有两种形式的委托,分别是 类委托 和 属性委托。

类委托

在Kotlin中类委托是如何实现的呢?我们先来看一个官方文档上的栗子:

interface Base {    fun print()}class BaseImpl(val x: Int) : Base {    override fun print() { print(x) }
}class Derived(b: Base) : Base by bfun main(args: Array<String>) {    val b = BaseImpl(10)
    Derived(b).print() // prints 10}

让我们对上面的代码进行分析。首先有一个接口 Base,类 BaseImpl 以及 Derived 都实现了 Base 接口。不知道你们又没有感到一丝的不和谐。一个类实现接口的时候得对接口中的所有方法进行实现(在Kotlin中如果接口提供了默认的实现方式,那么实现类可以不用对该方法进行实现),可是类Derived 并没有实现 Base接口中的 print 方法。

看了Kotlin所对应的Java代码,你就明白了:



实际上,编译器会自动帮你实行 Base 接口中的 print 方法。可编译器为什么和搞特例呢?对其他的类却没有这样进行照顾。一切的奥妙都是因为一个关键字:by

class Derived(b: Base) : Base by b

接口上面的Java代码,我们可以这么理解这行代码:Derived是委托者,而被委托者是Base接口的一个实现类。

属性委托

属性委托比类委托写起来要稍微复杂一些,照例,先看看官方所提供的栗子:

class Example {var p: String by Delegate()}
class Delegate {    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name} in $thisRef.'")
    }
}
val e = Example()e.p="Hello world"println(e.p)

属性委托不需要实现某个接口,只需要在被委托的类中对setValue(对应 var 属性)和 getValue 操作符进行重载。

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)

thisRef 参数表示委托对象的引用;

property 参数表示委托的属性;

value 参数表示为属性赋值。

注:属性委托实际上只需要 by 右侧的对象实现了 getValue 或 setValue(var 属性)操作符。因此在 by 的右侧也可以是一个函数,其返回值中带有 getValue 或 setValue 的实现。

在Kotlin中有两个接口 ReadOnlyProperty 和 ReadWriteProperty 接口方便我们进行相应的操作。我们可以像下面这行代码一样定义一个被委托的对象。

fun recode(value:String):ReadOnlyProperty<Any?,String> = object: Recode<String>()

在Kotlin的标准库中还提供了几个已经实现的委托属性:

一、延迟属性 lazy

fun main(args: Array<String>) {
    println(hello)
    println(hello)
}val hello:String by lazy(LazyThreadSafetyMode.NONE) {
    println("feintkotlin")    "hello world"}

当一个属性(val 类型的属性,延迟属性并没有实现setValue操作)被委托给延迟属性后,第一次执行该属性的get方法时会执行lazy后面的lambda表达式并返回结果,之后的调用就只会返回结果。

lazy属性默认是添加了同步锁的,如果你想让它可以同时在多个线程执行,可以像下面这样写:

val hello:String by lazy(LazyThreadSafetyMode.PUBLICATION)

或者,当你确认该属性始终运行在单线程下,也可以像下面这样关闭线程安全:

val hello:String by lazy(LazyThreadSafetyMode.NONE)

二、可观察属性 Observable

fun main(args: Array<String>) {    val user = User()
    user.name = "first"
    user.name = "second"}class User {    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

observable()接受两个参数,第一个参数是属性的初始值,第二个属性是当属性值发生改变后的处理程序。在这个处理程序中又有三个参数:属性、属性原来的值、属性当前的值

三、将属性委托给映射(Map)

fun main(args: Array<String>) {    val user = User(mapOf(            "name" to "John Doe",            "age"  to 25
    ))
}class User(val map: Map<String, Any?>) {    val name: String by map    val age: Int     by map
}

如果,属性的类型是 var ,则需要将Map 修改为 MutableMap。


更多

  1. 在Kotlin 1.1版本之后,可以将局部属性声明为委托属性。

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

原文链接:http://www.apkbus.com/blog-822717-72725.html

0人推荐
随时随地看视频
慕课网APP