提供委派(从Kotlin 1.1开始)
通过定一个provideDelegate操作符,你可以扩展创建对象的逻辑,对象的属性必须是委派的。如果对象在使用在右边语句语句时使用by关键字,并通过provideDelegate定义为成员或扩展函数,那么这个函数就会被调用来创建一个委派对象。
另一个使用provideDelegate的场景就是在属性创建时检查属性一致性,而不仅仅是getter和setter中进行检查。
例如:如果你想在绑定前先检查属性名,你可以这样写:
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
}
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)
}
provideDelegate方法的参数与getValue()一致:
- thisRef -- 必须是与属性一样的类型或是超类型
- property -- 必须是KProperty<*>或其超类
provideDelegate方法会在创建MyUI的每个属性时进行调用,并且马上检查其有效性。
上述例子来自Kotlin的官方文档,文档给的例子并不完整,完整例子如下:
class Delegate<T>(val id: T): ReadOnlyProperty<MyUI, T> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): T {
return id
}
}
class ResourceID<T>(val desc: T) {
companion object {
val image_id = ResourceID("1111111")
val text_id = ResourceID("2222222")
}
}
class ResourceLoader<T>(val resId: ResourceID<T>) {
operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
return Delegate(resId.desc)
}
private fun checkProperty(thisRef: MyUI, name: String) {
println("MyUI instance:" + thisRef + ", Preperty name:" + name)
}
}
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> {
return ResourceLoader(id)
}
class MyUI {
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
fun main(args: Array<String>) {
val mu = MyUI();
println("image-id = " + mu.image)
println("txt-id = " + mu.text)
}
如果不能拦截property和它的委派之间的绑定,为了达到这个类似的功能,你需要显示的传递这个属性名,这并不是很方便。
// 不使用"provideDelegate"方法
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)
// 创建委派实例
}
在使用provideDelegate的代码中,provideDelegate方法会在初始化prop$delegate属性时调用。和上述不使用provideDelegate方法的属性定义val prop: Type by MyDelegate()相比。
class C {
var prop: Type by MyDelegate()
}
// 当'provideDelegate'可用时生成如下代码
class C {
// 调用"provideDelegate"方法来创建额外的委派属性
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.getValue(this, this::prop)
}
需要注意的是provideDelegate方法会影响辅助属性的创建,并不会影响getter和setter。