我明白为什么 Kotlin 不允许初始化扩展属性,例如
val SomeClass.anExtensionProperty = "can't do this"
因为没有支持字段来保存该值。我知道你可以使用
get()
'ter 因为它不保存状态,如下所示:
val SomeClass.anExtensionProperty get() = "you CAN do this"
因为它是动态计算的并且没有状态可以保存。
所以我很惊讶地发现你可以做到这一点:
val SomeClass.anExtentionsionProperty by lazy{
"you CAN do this, but why?"
}
在我看来,当正常使用
by
委托(不使用扩展)时,您正在“设置”一个属性来引用委托,这意味着存储对实例化对象的引用(即状态)。这似乎违反了对扩展属性的限制。
为什么允许这样做?幕后发生了什么?
当我们使用
by
运算符时,编译器会计算右侧的值并将委托存储在当前作用域中。如果我们在函数中执行此操作,委托将存储为局部变量。如果我们在班级中,则将其存储为成员。如果我们位于顶级范围内,则它存储在顶级范围中。
您的案例是最后一个。我们位于顶级范围内,因此我们在顶级范围内创建一个委托。它不会为每个实例创建单独的惰性对象。它创建一个全局实例并在所有实例之间共享它。我们可以很容易地验证这一点:
fun main() {
val c1 = SomeClass()
println(c1.anExtentionsionProperty)
val c2 = SomeClass()
println(c2.anExtentionsionProperty)
}
class SomeClass
val SomeClass.anExtentionsionProperty by lazy{
println("hello")
"you CAN do this, but why?"
}
此代码仅打印一次“hello”,因为两个实例共享相同的惰性。
我们也可以在字节码中验证这一点:
public final class Test1Kt {
private final static Lkotlin/Lazy; anExtentionsionProperty$delegate
}
它在
Test1Kt
类中创建了一个静态字段(文件名为 test1.kt
),并在那里存储了 Lazy
对象。然后它在 getAnExtentionsionProperty
方法中访问它。