class FieldProperty<R, T : Any>(
val initializer: (R) -> T = { throw IllegalStateException("Not initialized.") }
) {
private val map = WeakIdentityHashMap<R, T>()
operator fun getValue(thisRef: R, property: KProperty<*>): T =
map[thisRef] ?: setValue(thisRef, property, initializer(thisRef))
operator fun setValue(thisRef: R, property: KProperty<*>, value: T): T {
map[thisRef] = value
return value
}
}
用法示例:
var Int.tag: String by FieldProperty { "$it" }
fun main(args: Array<String>) {
val x = 0
println(x.tag) // 0
val z = 1
println(z.tag) // 1
x.tag = "my tag"
z.tag = x.tag
println(z.tag) // my tag
}
在类中定义时,映射可以独立存储在类的实例中,也可以存储在共享的委托对象中:
private val bATag = FieldProperty<Int, String> { "$it" }
class B() {
var A.someCounter: Int by FieldProperty { 0 } // independent for each instance of B
var A.tag: String by bATag // shared between the instances, but usable only inside B
}
4 回答
不 - documentation解释了这个:
和
考虑扩展函数/属性作为调用静态函数和传入值的语法糖有希望使这一点清楚 .
您可以使用重写的getter和setter创建扩展属性:
但是您无法使用支持字段创建扩展属性,因为您无法将字段添加到现有类 .
有no way to add extension properties with backing fields到类,因为扩展do not actually modify a class .
您只能使用自定义getter(和
var
的setter)或delegated property定义扩展属性 .但是,如果您需要定义一个扩展属性,其行为就像它具有支持字段一样,则委派属性会派上用场 . 我们的想法是创建一个存储对象到值映射的属性委托:
使用标识,而不是
equals()
/hashCode()
来实际存储每个对象的值,如IdentityHashMap
;没有阻止密钥对象被垃圾收集(使用weak references),就像
WeakHashMap
一样 .遗憾的是,JDK中没有
WeakIdentityHashMap
,因此您必须实现自己的(或采用complete implementation) .然后,基于此映射,您可以创建满足property delegates requirements的委托类 . 这是一个非线程安全实现的示例:
用法示例:
在类中定义时,映射可以独立存储在类的实例中,也可以存储在共享的委托对象中:
另外,请注意由于装箱而导致Java的原始类型的身份is not guaranteed .
我怀疑这个解决方案的性能明显比常规字段差,很可能接近正常
Map
,但这需要进一步测试 .有关可空属性支持和线程安全实现,请参阅here .
您不能添加字段,但可以添加一个属性,该属性委托给对象的其他属性/方法以实现其访问器 . 例如,假设您要将
secondsSinceEpoch
属性添加到java.util.Date
类,则可以编写