Delegated Properties
2017-05-23 本文已影响15人
lhyz
1.自定义代理
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef,thanks you for delegating"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to in $thisRef")
}
}
上面的示例就是针对String这个类型的属性的代理类实现,用法如下
class Example {
var p: String by Delegate()
}
在此类被使用的时候,对p
变量的幅值和取值都会经过代理类
val e = Example()
e.p = "a"
print(e.p)
此时会依次打印出
a has been assigned to in Example@79fc0f2f
Example@79fc0f2f,thanks you for delegating
2.标准代理库
import kotlin.properties.Delegates
Kotlin标准库中实现了一个代理工厂类Delegates,可以很方便的被使用,以下是官网上介绍的几种
lazy (延迟赋值?)
val v: String by lazy {
println("delegate")
"hello"
}
fun main(args: Array<String>) {
print(v)
print(v)
}
可以看到的是lazy实现的是一种lambda调用方法。
lazy在变量第一次被赋值之前插入一段操作,因此最后的结果是输出了一次delegate,两次hello
observable (观察者?)
class User {
var name: String by Delegates.observable("default") {
property, oldValue, newValue ->
println("$property $oldValue -> $newValue")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
Delegates.observable定义两个参数,一个是初始值,一个就是监听值的修改的方法,使用lambda表达式的参数就是property, oldValue, newValue这三个,顾名思义即可。
因此输出的结果是:
var User.name: kotlin.String default -> first
var User.name: kotlin.String first -> second
3.存储属性map
class User(map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val user = User(mapOf(
"name" to "john",
"age" to 11
))
println(user.name)
println(user.age)
}
使用map存储属性键值对进行代理初始化
4.代理类属性 总结
要实现一个代理类所要所的事如下:
- 对于只读属性,需要实现
getValue
方法,参数包括 :thisRef
- 必须是所代理类型或其超类,property
-必须是KProperty<*>
或其超类 - 对于可变类型,需要实现
getValue
和setValue
方法,setValue
方法除了最后一个附加参数必须是其代理类型之外,其他类型与getValue
相同
如果不清楚具体写法,可以直接通过实现标准库中的接口来定义自己的代理类:下面就是标准库中给出的接口
interface ReadOnlyProperty<in R, out T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
}
interface ReadWriteProperty<in R, T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
翻译规则(看看就行了)
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler instead:
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)
}
*Providing a delegate
一个类似getValue
的代理接口provideDelegate
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// create delegate
}
private fun checkProperty(thisRef: MyUI, name: String) { ... }
}
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
class MyUI {
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
原因是如果要实现相同的效果,不用此方法而是使用getValue
方法的话不太优雅,例如:
// Checking the property name without "provideDelegate" functionality
class MyUI {
val image by bindResource(ResourceID.image_id, "image")
val text by bindResource(ResourceID.text_id, "text")
}
fun <T> MyUI.bindResource(
id: ResourceID<T>,
propertyName: String
): ReadOnlyProperty<MyUI, T> {
checkProperty(this, propertyName)
// create delegate
}