概述
- 类委托
委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它。这是官方对委托的描述,可能比较抽象但当你了解其意义和使用,会发现委托所带来的方便会让你爱不释手,下面我们来开始学习委托吧,首先看一个官方的例子:
interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() { print(x) } } class Derived(b: Base) : Base by b
上述Derived类,继承了接口Base,理应实现接口Base中的方法,但此处不仅没有实现反而出现了 by b ,因为Derived的类的主构造函数传入的对象本身是实现了Base接口的,这里的by 代表 b 将会在 Derived 中内部存储, 编译器将生成转发给 b 的所有 Base 的方法。这句话更直观的理解是创建的Derived的对象所执行的方法,都委托给b,相当于调用对象b的方法一样(针对实现Base接口的方法才会委托)。
Derived 和 BaseImpl类中添加一个方法,看看此时会调用哪个方法:
class BaseImpl(val x: Int) : Base { override fun print() { Log.e("================",x.toString()) } fun log(){ Log.e("================","BaseImpl===============") } } class Derived(b: Base) : Base by b{ fun log(){ Log.e("================","Derived===============") } }
创建Derived 的对象,并分别调用print()和log()方法,运行结果:
02-28 02:20:40.062 4226-4226/com.example.administrator.kotlinpractise E/================: 10 02-28 02:20:40.062 4226-4226/com.example.administrator.kotlinpractise E/================: Derived===============
此处可以看到print方法中国输出的是BaseImpl中的方法,而log输出的是Derived的方法,现在知道上面最后括号中那句话的意识乐吧。
- 委托属性:
class Example { var e : String by Delege() } class Delege{ operator fun getValue(thisRef: Any?, property: KProperty<*>) : String { Log.e("getValue============","getValue") return "$thisRef" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){ Log.e("setValue============","$value") }
现在创建并负值Example的对象:
var example = Example() Log.e("=========", example.e) example.e = "New Example" Log.e("=========", example.e)
现在来看看打印的日志:
02-28 03:02:04.826 5876-5876/com.example.administrator.kotlinpractise E/getValue============: getValue 02-28 03:02:04.826 5876-5876/com.example.administrator.kotlinpractise E/=========: com.example.administrator.kotlinpractise.Example@3d2e49e 02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/setValue============: New Example 02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/getValue============: getValue 02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: com.example.administrator.kotlinpractise.Example@3d2e49e对于一个只读属性(即 val 声明的),委托必须提供一个名为 getValue 的函数,该函数接受以下参数:
thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型;
property —— 必须是类型 KProperty<*> 或其超类型。
这个函数必须返回与属性相同的类型(或其子类型)。
对于一个可变属性(即 var 声明的),委托必须额外提供一个名为 setValue 的函数,该函数接受以下参数:
thisRef —— 同 getValue();
property —— 同 getValue();
new value —— 必须和属性同类型或者是它的超类型。
getValue() 或/和 setValue() 函数可以通过委托类的成员函数提供或者由扩展函数提供。 当你需要委托属性到原本未提供的这些函数的对象时后者会更便利。 两函数都需要用 operator 关键字来进行标记。
- 可观察属性 Observable
接受两个参数:初始值和修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值,在每次为变量赋值的时候委托的程序会执行
var observer : String by Delegates.observable("A"){ property,old,new -> Log.e("$old=====","$new") } Log.e("=========",observer) observer = "test" Log.e("=========",observer)
输出结果:
02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/=========: A 02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/A=====: test 02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/=========: test
- vetoable()
var observer: String by Delegates.vetoable("A") { property, old, new -> new.equals("NEW") } Log.e("=========", observer) observer = "test" Log.e("=========", observer) observer = "NEW" Log.e("=========", observer)
输出结果:
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: A 02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: A 02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: NEW
- 把属性储存在映射中
一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。 在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。
class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map }在这个例子中,构造函数接受一个映射参数:
val user = User(mapOf( "name" to "John Doe", "age" to 25 ))
委托属性会从这个映射中取值(通过字符串键——属性的名称):
println(user.name) // Prints "John Doe"println(user.age) // Prints 25
到这里大家对委托应该有了初步的了解,个人感觉委托在开发中还是很实用的,比如一些需要需要读取和设置的数据,可以使用属性委托免去一些代码且逻辑性更好,之后还会继续出一遍关于委托更深入的应用,今天暂时到这吧。