Kotlin委托属性实现'provideDelegate'操作符函数,但方法从未调用过
我有一个基本类型DbType
这是扩展我的模型是这样的:
interface DbType { fun <T : Any> field() = PropertyMapper<T>() }
PropertyMapper
类是这样实现的:
class PropertyMapper<out T : Any> internal constructor() { @Suppress("UNCHECKED_CAST") operator fun getValue(inst: DbType, prop: KProperty<*>): T { return ImplementationDetail.values?.get(prop.name) as T? ?: throw NullPointerException("No such ${prop.name} in type ${inst::class}") } operator fun provideDelegate(inst: DbType, prop: KProperty<*>): ReadOnlyProperty<DbType, T> { TODO() } }
根据kotlin文档中的这个条目,我应该能够通过在属性委托给的类中添加一个运算符函数provideDelegate
来提供自己的ReadOnlyProperty<R, T>
。 但是,当我实例化一个实现/扩展DbType
的对象时,运算符函数provideDelegate
从不被调用。 “getValue”部分工作正常,但我想拦截创建对象时的属性委托创建。 任何想法我做错了吗? 这是一个示例类:
class BasicUserInfo : DbType { val username by field<String>() }
你确定它没有被叫? 我在你的代码中发现错误的是,当你实现了provideDelegate
运算符,并返回一些委托对象,而不是它的接收者,你不需要getValue
操作符来实现,因为它不会被使用,它的对象provideDelegate
返回将用于属性访问(见下文)。
否则,你的代码是有效的。 我稍微修改了你的例子,使其更清楚发生了什么:
class PropertyMapper<out T : Any> internal constructor() { operator fun getValue(inst: DbType, prop: KProperty<*>): T { println("PropertyMapper :: getValue") // note: never printed TODO() } operator fun provideDelegate(inst: DbType, prop: KProperty<*>): ReadOnlyProperty<DbType, T> { println("PropertyMapper :: provideDelegate") return object : ReadOnlyProperty<DbType, T> { override fun getValue(thisRef: DbType, property: KProperty<*>): T { println("anonymous ReadOnlyProperty :: getValue") TODO() } } } }
DbType
和BasicUserInfo
保持不变。
现在,当我创建一个BasicUserInfo
实例并获取其username
(使用表达式BasicUserInfo().username
)时,我得到以下输出:
PropertyMapper :: provideDelegate anonymous ReadOnlyProperty :: getValue Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented. ...
如果我只创建实例,那只是第一行。
请参阅此代码的可运行演示 。
我想通了:这似乎是在kotlin插件或gradle中的错误。 如果我使用PropertyMapper
构建一个干净的项目,只有一个名为getValue
运算符方法,它将按预期工作:在访问该字段时调用PropertyMapper#getValue
。
然后,当我删除该方法,而是使用provideDelegate
运算符方法,它会失败,一个NoSuchMethodError
(永远不会调用PropertyMapper#provideDelegate
方法)。 如果我清理(手动删除构建/出文件夹)并重新构建该设置,它运行得很好。