提供委派(从Kotlin 1.1开始)

通过定一个provideDelegate操作符,你可以扩展创建对象的逻辑,对象的属性必须是委派的。如果对象在使用在右边语句语句时使用by关键字,并通过provideDelegate定义为成员或扩展函数,那么这个函数就会被调用来创建一个委派对象。

另一个使用provideDelegate的场景就是在属性创建时检查属性一致性,而不仅仅是gettersetter中进行检查。

例如:如果你想在绑定前先检查属性名,你可以这样写:

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()一致:

  1. thisRef -- 必须是与属性一样的类型或是超类型
  2. 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。

results matching ""

    No results matching ""