类委托
Kotlin通过by
关键字可以简单的实现委托模式。
委托模式,用java的话来说,就是通过组合,让类A持有其他类B的实例,然后类A中的部分方法实际上都是通过调用的类B这个实例的方法来实现的。
kotlin实现委托模式
Activity中的onClick方法,实际上是委托给OnClickListener的实例来实现的,简单易懂。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21interface Listener {
fun onClick()
}
class OnClickListener : Listener {
override fun onClick() {
print("OnClickListener")
}
}
class Activity : Listener {
val mListener = OnClickListener()
override fun onClick() {
mListener.onClick()
}
}
fun main(args: Array<String>) {
var act = Activity()
act.onClick()
}
by关键字简洁地实现委托模式
上面的实现,乍一看跟java没两样,可kotlin要的就是简洁、优雅,要是还跟java一样罗里吧嗦,怎么体现这门语言的逼格?所以就引入了 by
关键字来实现委托模式。
原理都差不多,在类Activity的构造方法中传入OnClickListener实例即可,直接上代码吧。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17interface Listener {
fun onClick()
}
class OnClickListener : Listener {
override fun onClick() {
print("OnClickListener")
}
}
class Activity(listener: OnClickListener) : Listener by listener
fun main(args: Array<String>) {
var listener = OnClickListener()
var act = Activity(listener)
act.onClick()
}
代理属性
Kotlin也支持代理属性,语法定义如下:val/var <property name>: <Type> by <expression>
- var/val:属性类型(可变/只读)
- name:属性名称
- Type:属性的数据类型
- expression:代理类
1 | class Example { |
输出1
2Example@25f38edc, thank you for delegating 'p' to me!
NEW has been assigned to 'p in Example@25f38edc.'
Kotlin 标准库为几种常用的代理提供了工厂方法:
- 延迟加载属性(lazy property): 属性值只在初次访问时才会计算,然后保存结果,后面直接返回这个结果
- 可观察属性(observable property): 属性发生变化时, 可以向监听器发送通知
- 将多个属性保存在一个 map 内, 而不是保存在多个独立的域内
延迟加载的属性
lazy() 是一个接受 lamdba 并返回一个实现延迟属性的代理:第一次调用 get() 执行 lamdba 并传递 lazy() 并存储结果,以后每次调用 get() 时只是简单返回之前存储的值。
注意: var类型属性不能设置为延迟加载属性,因为在lazy中并没有setValue(…)方法
lazy操作符是线程安全的。如果在不考虑多线程问题或者想提高更多的性能,也可以使用lazy(LazyThreadSafeMode.NONE){ … }
1 | fun lazyinit(): Int { |
输出1
2
3lazy is called
age: 1
age: 1
可观察的属性
Delegates.observable() 需要两个参数:一个初始值和一个用于修改的 handler
handler有三个参数:一个将被赋值的属性,旧值,新值。1
2
3
4
5
6
7
8
9
10
11
12
13var name: String by Delegates.observable("initName", {
kProperty, oldName, newName ->
println("kProperty:${kProperty.name} | oldName:$oldName | newName:$newName")
})
fun main(args: Array<String>) {
println("name: $name")
name = "newName1"
name = "newName2"
println("name: $name")
}
输出1
2
3
4name: initName
kProperty:name | oldName:initName | newName:newName1
kProperty:name | oldName:newName1 | newName:newName2
name: newName2
Delegates.vetoable() 也需要两个参数:一个初始值和一个用来在保存新值之前做一些条件判断,来决定是否将新值保存。
修改上述部分代码:1
2
3
4
5var name: String by Delegates.vetoable("initName", {
kProperty, oldName, newName ->
println("kProperty:${kProperty.name} | oldName:$oldName | newName:$newName")
newName.contains("1")
})
输出1
2
3
4name: initName
kProperty:name | oldName:initName | newName:newName1
kProperty:name | oldName:newName1 | newName:newName2
name: newName1
在 Map 中存储属性
把属性值存储在 map 中是一种常见的使用方式,这种操作经常出现在解析 JSON 或者其它动态的操作中。这种情况下你可以使用 map 来代理它的属性。
1 | class User(val map: Map<String, Any?>) { |
输出1
2John Doe
25
var 属性可以用 MutableMap 代替只读的 Map1
2
3
4class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}