子类的 Kotlin 变量初始化在初始化值为 0 的变量时表现得很奇怪

PRA*_*NGH 17 polymorphism overriding initialization class kotlin

我创建了以下类层次结构:

open class A {
    init {
        f()
    }

    open fun f() {
        println("In A f")
    }
}

class B : A() {
    var x: Int = 33

    init {
        println("x: " + x)
    }

    override fun f() {
        x = 1
        println("x in f: "+ x)
    }

    init {
        println("x2: " + x)
    }
}

fun main() {
    println("Hello World!!")
    val b = B()
    println("in main x : " + b.x)
}
Run Code Online (Sandbox Code Playgroud)

这段代码的输出是

open class A {
    init {
        f()
    }

    open fun f() {
        println("In A f")
    }
}

class B : A() {
    var x: Int = 33

    init {
        println("x: " + x)
    }

    override fun f() {
        x = 1
        println("x in f: "+ x)
    }

    init {
        println("x2: " + x)
    }
}

fun main() {
    println("Hello World!!")
    val b = B()
    println("in main x : " + b.x)
}
Run Code Online (Sandbox Code Playgroud)

但是如果我改变了xfrom的初始化

var x: Int = 33
Run Code Online (Sandbox Code Playgroud)

var x: Int = 0
Run Code Online (Sandbox Code Playgroud)

与上面的输出相比,输出显示了该方法的调用:

Hello World!!
x in f: 1
x: 33
x2: 33
in main x : 33
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么初始化 with0会导致与另一个值不同的行为?

Sxt*_*nna 19

超类在子类之前初始化。

B的构造函数调用调用A的构造函数,调用函数f打印“x in f:1”,A初始化后,B的其余部分也被初始化。

所以本质上,该值的设置正在被覆盖。

(当你在 Kotlin 中用它们的零值初始化基元时,它们在技术上根本不初始化)

您可以通过更改签名来观察这种“覆盖”行为

var x: Int = 0var x: Int? = 0

由于x不再是原语int,该字段实际上被初始化为一个值,产生输出:

Hello World!!
x in f: 1
x: 0
x2: 0
in main x : 0
Run Code Online (Sandbox Code Playgroud)

  • *当您在 Kotlin 中用零值初始化原语时,从技术上讲它们根本不初始化*是我想读的……谢谢! (5认同)
  • @Kroppeb 这只是 Java,同样的行为可以单独在 Java 代码中观察到。与Kotlin无关 (2认同)

van*_*hek 8

文档中描述了此行为 - https://kotlinlang.org/docs/reference/classes.html#derived-class-initialization-order

如果在基类初始化逻辑中使用了这些属性中的任何一个(直接或间接,通过另一个覆盖的开放成员实现),则可能会导致不正确的行为或运行时失败。因此,在设计基类时,应避免在构造函数、属性初始值设定项和 init 块中使用开放成员。

更新:

有一个错误会导致这种不一致 - https://youtrack.jetbrains.com/issue/KT-15642

当一个属性被分配为超级构造函数内的虚函数调用的副作用时,如果初始化表达式是类型默认值(空,原始零),它的初始化器不会覆盖该属性。