在Kotlin委托属性上使用Context Receiver报错,是否支持该用法?
我实现了一个带有Context Receiver的属性委托类LoggingPropertyDelegate,代码如下:
class LoggingPropertyDelegate<T, V, L : Log>( private var value: V, private val toLog: T.() -> L ) : ReadWriteProperty<T, V> { override fun getValue(thisRef: T, property: KProperty<*>) = value context(Logger) override fun setValue(thisRef: T, property: KProperty<*>, value: V) { this.value = value log(toLog(thisRef)) } }
但在将其用于属性var myValue: Int by LoggingPropertyDelegate(0, { InfoLog("Changed to $myValue") })时,编译器提示委托没有合适的set函数。移除setValue方法上的context修饰后,一切正常。请问是否无法在属性委托上使用Context Receiver?
原因分析
Kotlin标准库中的ReadWriteProperty接口定义的setValue方法没有任何上下文约束。当你给重写的setValue添加context(Logger)修饰符后,这个方法的签名实际上和接口要求的签名不匹配——你相当于定义了一个带有额外上下文要求的重载方法,而非真正实现接口的setValue。因此编译器会认为你的委托类没有实现ReadWriteProperty接口要求的setValue方法,从而报错。
解决办法
如果想在属性委托的setValue中使用Context Receiver,需要调整接口定义或委托类的实现方式:
自定义带上下文约束的属性委托接口
定义一个带有Logger上下文的自定义接口,让委托类实现这个接口:context(Logger) interface LoggerReadWriteProperty<T, V> { fun getValue(thisRef: T, property: KProperty<*>): V fun setValue(thisRef: T, property: KProperty<*>, value: V) } class LoggingPropertyDelegate<T, V, L : Log>( private var value: V, private val toLog: T.() -> L ) : LoggerReadWriteProperty<T, V> { override fun getValue(thisRef: T, property: KProperty<*>) = value override fun setValue(thisRef: T, property: KProperty<*>, value: V) { this.value = value log(toLog(thisRef)) } }这种方式下,使用该委托的属性在赋值时必须处于
Logger上下文范围内,否则编译器会报错。将上下文约束添加到委托类本身
也可以直接给委托类添加context(Logger)约束,这样类内的所有方法都会自动继承该上下文:context(Logger) class LoggingPropertyDelegate<T, V, L : Log>( private var value: V, private val toLog: T.() -> L ) : ReadWriteProperty<T, V> { override fun getValue(thisRef: T, property: KProperty<*>) = value override fun setValue(thisRef: T, property: KProperty<*>, value: V) { this.value = value log(toLog(thisRef)) } }这种方式要求创建委托实例时必须处于
Logger上下文,后续属性赋值时也需要该上下文。
内容的提问来源于stack exchange,提问作者alturkovic

