kotlin属性代理官方文档地址:http://kotlinlang.org/docs/reference/delegated-properties.html
kotlin的代理模式需要实现一个接口,而属性代理只需要实现getValue和setValue方法即可。
语法形式:val/var
其中
代理了
的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.")
}
}
fun main(args: Array) {
val e = Example()
println(e.p)
}
输出:Example@33a17727, thank you for delegating ‘p’ to me!
当我们给p赋值的时候,setValue会被调用
e.p = "NEW"
输出:NEW has been assigned to ‘p’ in Example@33a17727.
自定义属性代理也可以实现ReadWriteProperty、ReadOnlyProperty接口。
public interface ReadOnlyProperty {
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
*/
public operator fun getValue(thisRef: R, property: KProperty<*>): T
}
/**
* Base interface that can be used for implementing property delegates of read-write properties.
*
* This is provided only for convenience; you don't have to extend this interface
* as long as your property delegate has methods with the same signatures.
*
* @param R the type of object which owns the delegated property.
* @param T the type of the property value.
*/
public interface ReadWriteProperty {
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
*/
public operator fun getValue(thisRef: R, property: KProperty<*>): T
/**
* Sets the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @param value the value to set.
*/
public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
二.lazy
当变量第一次被使用时才进行初始化,可以实现懒加载。
lazy方法返回一个Lazy
public fun lazy(initializer: () -> T): Lazy = SynchronizedLazyImpl(initializer)
当第一次获取lazyValue的值时,会先初始化lazyValue,然后把值保存起来,下一次获取lazyValue时直接获取保存好的值即可。
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array) {
println(lazyValue)
println(lazyValue)
}
输出:
computed!
Hello
Hello
初始化lazyValue的过程默认是线程安全的,通过synchronized锁来保证。
不过这肯定是影响性能的,如果我们确信lazyValue的初始化不会涉及到多线程,那么我们可以传入LazyThreadSafetyMode.NONE来取消同步锁。
val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {
println("computed!")
"Hello"
}
LazyThreadSafetyMode有三种模式:SYNCHRONIZED(默认模式)、PUBLICATION、NONE
其中PUBLICATION模式使用了AtomicReferenceFieldUpdater(原子操作)允许多个线程同时调用初始化流程。
三.observable
变量被赋值时会发出通知。
Delegates.observable可以传入两个参数,一个是初始值,另一个是变量被赋值时的handle方法。
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array) {
val user = User()
user.name = "first"
user.name = "second"
}
输出:
-> first
first -> second
注意:只要 user.name被赋值,监听事件就会触发。
类似的还有 vetoable()
,只不过vetoable是在赋值前触发,observable是在赋值后触发。
vetoable还可以对赋值操作进行拦截。
import kotlin.properties.Delegates
class User {
var name: String by Delegates.vetoable("") { p, oldValue, newValue ->
if (newValue == "first"){
return@vetoable true // 返回true表示first可以赋值给name
}
return@vetoable false // 返回false表示拦截其他赋值操作。
}
}
fun main(args: Array) {
val user = User()
user.name = "first"
println(user.name)
user.name = "two"
println(user.name)
}
输出:
first
first
四.对map的代理
把map中某个key值映射为变量。
fun main(args: Array) {
val map = mutableMapOf("name" to "小明", "age" to 1)
var name: String by map
println(name)
name = "小红"
println(map["name"])
}
输出:
小明
小红
五.局部变量的代理(1.1版本新增)
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
六.提供代理(1.1版本新增)
我们可以干预代理类的创建过程。提供provideDelegate方法即可,该方法返回要创建的代理对象。
class ResourceDelegate : ReadOnlyProperty {
override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader(id: ResourceID) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty {
checkProperty(thisRef, prop.name)
// create delegate
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { ... }
}
class MyUI {
fun bindResource(id: ResourceID): ResourceLoader { ... }
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
provideDelegate方法有两个参数,参数1是代理属性所在的类,即image所在的类:MyUI。参数2是代理属性的具体参数对像(包括属性名称,属性类型等等)。