我遇到了一些关于kotlin顶级属性的问题,下面是示例代码:

// Situation 1

class TreeNode(var `val`: Int) {
    var left: TreeNode? = null
    var right: TreeNode? = null
}

val tree2 = TreeNode(2)
val tree1 = TreeNode(3).apply { left = tree2 } 

fun main() {
    println(tree1.left) // prints "TreeNode@..."
}
// Situation 2

class TreeNode(var `val`: Int) {
    var left: TreeNode? = null
    var right: TreeNode? = null
}

val tree1 = TreeNode(3).apply { left = tree2 } 
val tree2 = TreeNode(2)

fun main() {
    println(tree1.left) // prints "null"
}
// Situation 3

class TreeNode(var `val`: Int) {
    var left: TreeNode? = throw Exception("I am an exception")
    var right: TreeNode? = null
}

val tree1 = TreeNode(3).apply { left = tree2 } 
val tree2 = TreeNode(2)

fun main() {
    /* prints "Exception in thread "main" java.lang.ExceptionInInitializerError 
     * Caused by: java.lang.Exception: I am an exception ..."
     */
    println(tree1.left) 
}

看来

  1. 从上到下初始化顶级属性;
  2. 如果依赖属性没有初始化,它将被默认值替换;

我说得对吗?如果是,我想知道

  1. 有没有官方文件说这种行为?
  2. 如果依赖属性尚未初始化,为什么它宁愿用默认值替换它,而不是出现编译时错误或抛出运行时错误,即tree2未初始化?

谢谢

推荐答案

从上到下初始化顶级属性;

实际上,我在Kotlin/Core规范中找不到任何可以证实这一点的东西.我只能找到这Kotlin forum post个,它表示顶级属性初始化依赖于平台,并且您得到的唯一保证是在您访问属性之前将运行属性的初始化器,这并没有说任何其他属性的初始化器.

Kotlin不会给你任何保证,当顶级wine 店

如果你知道你在哪个目标平台上,你就有了更多

我不会撒谎,这对我来说也很奇怪!但从我的测试来看,当前的编译器版本1.7.10does似乎初始化了所有平台(JVM、JS、Native)上的顶级属性,除非它们是内联的.


如果依赖属性未初始化,它将被默认值替换

这也不是真的.如果依赖属性未初始化,则其获得的值为unspecified.根据spec(emphasis mine):

声明范围和语句范围之间的主要区别在于,语句范围中的名称是按出现顺序绑定的.不允许通过代码中(语法上)在绑定本身之前的标识符访问值.On the contrary, in declaration scopes it is fully allowed, although initialization cycles may occur leading to unspecified behaviour.

非脚本Kotlin文件的顶级作用域是"声明作用域",如该页前面所述.


由于您似乎在JVM上,在情况2中实际发生(但未指定)的是未初始化的tree2null(JVM上所有引用类型的默认值),并分配给left,当时也恰好是null.

对于情况3,根本没有特定于平台的行为.调用构造函数Tree次,然后运行初始化器left次,然后抛出异常,这就是程序的结束.它甚至没有达到apply部分.

充分证明了未初始化属性的未指定行为

fun main() {
    println(y)
}

val y = run { x }
val x = run { 1 }

在JVM上,它打印0.在Kotlin/Native上,它也打印0,尽管我可以想象它也可以打印该位置中发生的任何位.在Kotlin/JS上,它打印"未定义".


至于为什么它被设计为"未指定",可能是因为Kotlin团队还有其他更重要的事情需要关注.人们通常不会有太多相互依存的全球财产.See also this great answer by Eric Lippert.

Kotlin相关问答推荐

Kotlin—列出具有不同T的列表之间的操作'

可以从背景图像中点击图标吗?

在 Kotlin 中定义基于多态函数的泛型函数

如何优雅地声明一个StateFlow?

列表在 android WebView 中没有正确迭代

Gradle:无法创建 ExtensiblePolymorphicDomainObjectContainer

为什么 trySend 会发出假数据?

Kotlin 启动与启动(Dispatchers.Default)

Kotlin 中的as Long和.toLong()有什么区别?

伴随对象在变量更改时更改它的值

compareBy 如何使用布尔表达式在 kotlin 中工作

使用纯 Kotlin 函数作为 Junit5 方法源

如何将 kotlin 集合作为 varagrs 传递?

如何在 kotlin 中生成 json 对象?

Kotlin:sealed class cannot "contain" data classes?

在kotlin中,如何模拟封装回调函数?

禁用 IntelliJ kotlin * 导入?

如何将 CameraView 与 Jetpack Compose 一起使用?

为什么在 Kotlin 中return可以返回一个return?

Kotlin for assertThat(foo, instanceOf(Bar.class))