Kotlin - Class 继承;覆盖属性

Kotlin - Class Inheritance; Overriding properties

所以我今天开始学习 Kotlin,当我发现一些奇怪的东西时就开始 类。 当我 运行 此代码时:

fun main() {
    val dog_1 = Dog("Walrek", 7)
}

open class Animal(
    open val type: String,
    open val name: String,
    open val speed: Int,
) {
    init {
        println("(Ani init) New animal: $type called $name with $speed speed")
    }
}

class Dog(
    override val name: String,
    override val speed: Int,
): Animal("Dog", name, speed) {}

初始化方法会打印出:(Ani init) New animal: Dog called null with 0 speed.

起初我以为我搞砸了某个地方,不知何故我没有将参数正确传递到 Animal class,但后来我添加了这两行代码:

init {
    println("(Dog init) New animal: $type called $name with $speed speed")
}

变成Dogclass,然后

println(dog_1.name)

main 函数中,并将其作为输出:

(Ani init) New animal: Dog called null with 0 speed
(Dog init) New animal: Dog called Walrek with 7 speed
Walrek

所以现在我的问题是,为什么参数没有传递到 Animal class,但我可以在创建 Dog 实例后照常访问它们?

您应该收到编译器警告。你不应该在构造时使用 open 属性或函数(在 init 块或 属性 声明中),因为它会导致这种不需要的行为。这是在不使用 !!.

的情况下在 Kotlin 中获得 NullPointerException 的唯一方法

发生这种情况的原因是超类的构造函数(包括 init 块和 属性 声明)在子类之前 运行。由于您在子类中覆盖了 属性 name,因此 属性 name 的支持字段在超类的 init 时尚未初始化调用,所以 属性 returns 的默认 Java 字段值为 null。在 speed 的情况下,字段类型是 Java 原语 int 所以它的默认值为 0.

请注意,在您的特定示例中,没有必要覆盖这些属性,因为您无论如何都将这些值传递给超构造函数。您可以删除属性前的 open 关键字并声明您的狗:

class Dog(
    name: String,
    speed: Int,
): Animal("Dog", name, speed) {}

然后它将正常运行。