oop - 学习 Kotlin 中构造函数的语法

标签 oop kotlin extends

我正在完美地学习 Kotlin 编程语言。我尝试以不同的模式编写代码并尝试理解。但是,我不明白这件事。你能帮我吗? 在这里:

open class Parent {
    open val foo = 1
    init {
        println(foo)
    }
}
class Child: Parent() {
    override val foo =2
}
fun main() {
    Child()
}

在这段代码中,0 是输出。这会怎样?

最佳答案

这是关于构造的顺序 — 并且是一个很容易陷入的微妙陷阱。 (恐怕这个答案有点长,但是这里的问题很值得理解。)

这里有一些基本原则:

  • 父类(super class)初始化发生在子类初始化之前。这包括构造函数中的代码、init block 中的代码和属性初始化程序:所有这些都发生在父类(super class)之前子类中的任何一个。

  • Kotlin 属性由一个 getter 方法、一个 setter 方法(如果它是一个 var)、和一个支持字段组成(如果需要的话)。这就是您可以覆盖属性的原因;这意味着访问器方法被覆盖。

  • 在初始化为任何其他字段之前,所有字段最初都为 0/false/null值(value)。 (通常情况下,你不会看到,但这是一种罕见的情况。这与 C 等语言不同,在 C 语言中,如果你没有明确初始化一个字段,它可以保存随机值,具体取决于之前使用的内存为。)

从第一个原则来看,当您调用Child() 构造函数时,它将首先调用Parent() 构造函数。这会将父类(super class)的 foo 字段设置为 1,然后获取 foo 属性并将其打印出来。之后,Child 初始化发生,在本例中只是将其 foo 字段设置为 2。

这里的陷阱是,您实际上有两个 foo!

Parent 定义了一个名为 foo 的属性,它获取访问器方法和一个支持字段。但是 Child 定义了它自己的名为 foo 的属性,覆盖了 Parent 中的属性——那个覆盖了访问器方法,并获得了自己的支持字段作为好吧。

由于该覆盖,当父级的 init block 引用 foo 时,它会调用 Child 覆盖的 getter 方法,以获取 Child 的支持字段的值。该字段尚未初始化!因此,如上所述,它仍然保持其初始值 0,这是 Child getter 返回的值,因此也是 Parent 构造函数打印出的值。

所以这里真正的问题是您在初始化之前访问子类字段。这个问题说明了为什么这是一个非常糟糕的主意!作为一般规则:

构造函数/初始化器不应该访问可以被子类覆盖的方法或属性。

IDE 可以帮助您解决这个问题:如果您将代码放入 IntelliJ,您会看到 foo 的用法标有警告 'Accessing non-final property foo在构造函数中'。这告诉你这种问题是可能的。

当然,还有一些更微妙的情况 IDE 可能无法警告您,例如构造函数调用非开放方法调用开放方法。所以需要小心。

有些情况下您可能需要打破该规则 — 但这种情况非常罕见,您应该非常仔细地检查以确保不会出错(即使后来有人出现并创建了一个新的子类)。并且您应该在评论/文档中非常清楚地说明发生了什么以及为什么需要它。

关于oop - 学习 Kotlin 中构造函数的语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65858569/

相关文章:

php - 在函数中返回 parent::function

android - kotlin-非 Activity 类的访问 View

javascript - 扩展状态 react

java - android访问扩展类中的对象

java - 在扩展方法中获取 NullPointerException - Java

oop - 为什么接口(interface)或抽象类有用? (或者为了什么?)

python - 向父类中的函数添加条件更好,还是为每个子类定义单独的函数更好?

android - 非法状态异常 : RecyclerView is null inside of Fragment within NavigationDrawer activity

android - 由于 ModelClass 在 KotlinMultiplatform 应用程序中不是可序列化或可解析的,导致 nav_graph 参数错误

PHP 迭代器 - 迭代完成时运行函数