一、Delegation 类的代理
代理模式被证明是实现继承的一个很好替代方案,代理模式使得我们可以用聚合来替代继承,将代理和真实实现分离开来,以达成解耦的目的。
Kotlin对代理模式内置支持,且零模版代码化。下面代码中代理类Derived通过实现Base接口,把其所有public的成员代理给一个指定的对象b
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print()
}
上面代码中,对象b会被保存在Derived类内部,并且编译器会生成接口Base中的所有方法,来调用对象b的相应方法。
Derived类反编译成的Java代码如下:从中可以看出Kotlin的内置代理支持,实现了零模版代码。
public final class Derived implements Base {
// $FF: synthetic field
private final Base $$delegate_0;
public Derived(@NotNull Base b) {
Intrinsics.checkParameterIsNotNull(b, "b");
super();
this.$$delegate_0 = b;
}
public void print() {
this.$$delegate_0.print();
}
}
⚠️注意:如果在代理类中手动实现了接口中的某一个方法或者属性变量,则最终调用时,会覆盖代理对象中的方法和变量
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
override fun print() { println("abc") }
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
//输出
abc
Message of Derived
二、Delegated Properties 属性的代理
属性代理赋予了属性富有变化的活力,有以下三种属性委托方式:
1. 延迟属性 lazy properties : 其值只在首次访问时计算
2. 可观察属性 observable properties :监听器会收到有关此属性变更的通知
3. 把多个属性存在一个映射(map)中,而不是每个存在单独的字段中
Kotlin中的属性代理语法定义如下:
val/var <property name>: <Type> by <expression>
expression就是代理,属性property的get() 和 set()方法会代理给expression的getValue() 和 setValue()方法,属性代理不需要实现接口。
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()
println(e.p)
//输出
Example@33a17727, thank you for delegating ‘p’ to me!
Kotlin标准库通过工厂方法提供了一些有用的代理,包括lazy() 、observable()、voteable()、notnull()
1. 懒加载属性代理 lazy properties
懒加载属性在第一次调用到时会计算其值,并保存下来,下此调用时直接返回保存的值。
lazy()函数接受一个lambda表达式,返回一个Lazy<T>的实例对象,该对象用以代理一个懒加载属性。lazy()函数接受的ambda表达式的值就是其属性的计算值。
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
//输出
computed!
Hello
Hello
lazy()函数可以接收一个LazyThreadSafetyMode参数,默认情况下,对lazy属性的求值是同步的(synchronized),即LazyThreadSafetyMode.SYNCHRONIZED , 如果多个线程可以同时计算属性值,可以使用LazyThreadSafetyMode.PUBLICATION,
如果属性的初始化是确定在一个线程里,可以使用LazyThreadSafetyMode.NONE来提高性能,因为省去了保证线程安全的开销。
2. 可观察属性代理 Delegates.observable()
Delegates.observable()有两个参数,一个初始值,一个修改后的操作,每次值的改变都会调用该操作
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
//输出
<no name> -> first
first -> second
3. 可否决属性代理 Delegates.voteable()
当把属性代理给Delegates.voteable()这个函数时,可以通过onChange函数返回值是否为true,来选择属性的值是否需要改变。
var name: String by Delegates.vetoable("<no name>") {
prop, old, new ->
true // 或者false
}
4. 非空属性代理 Delegates.notNull()
//将属性name限制为非空,如果赋值为null,编译器报错
var name: String by Delegates.notNull()
5. 属性代理给Map映射
在程序中,解析json或者做一些其他动态的事情时,可以使用该方法,将map对象本身当作属性的代理
class User(map: MutableMap<String, Any?>) {
val name: String by map
val age: Int by map
}
val user = User(mutableMapOf(
"name" to "John Doe",
"age" to 25
))
fun main(args: Array<String>) {
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
}