kotlin - 在基类初始化(直接或间接)中使用覆盖属性的示例是什么?
问题描述
这意味着,在基类构造函数执行时,派生类中声明或覆盖的属性尚未初始化。如果在基类初始化逻辑中使用了这些属性中的任何一个(直接或间接地,通过另一个覆盖的开放成员实现),则可能导致不正确的行为或运行时故障。因此,在设计基类时,应避免在构造函数、属性初始值设定项和 init 块中使用开放成员。
我正在从 Kotlin 文档中学习继承,我被困在这里。有另一个帖子问了一个关于这个的问题,但答案正是文档以不同的方式所说的。
- 需要明确的是,我理解了构造函数和继承之间的数据流。我无法理解的是我们如何在基类初始化中使用被覆盖的属性。它说
它可能直接或间接发生
这实际上意味着什么?基类如何能以某种方式访问派生类中被覆盖的属性?
- 还有,它说
因此,您应该避免在构造函数、属性初始值设定项和 init 块中使用开放成员。
那么我们如何才能正确使用开放属性呢?
编辑评论:
fun main ()
{
val d = Derived("Test2")
}
open class Base()
{
open val something:String = "Test1"
init
{
println(something) //prints null
}
}
class Derived(override val something: String): Base()
解决方案
这实际上意味着什么?基类如何能以某种方式访问派生类中被覆盖的属性?
这是一种直接的方法:
abstract class Base {
abstract val something: String
init {
println(something)
}
}
class Child(override val something: String): Base()
fun main() {
Child("Test") // prints null! because the property is not initialized yet
}
这会打印null
,这对于不可为空的String
属性来说是非常糟糕的。
因此,您应该避免在构造函数、属性初始值设定项和 init 块中使用开放成员。
那么我们如何才能正确使用开放属性呢?
您可以在基类的常规方法(或自定义属性 getter)中使用这些属性:
abstract class Base {
abstract val something: String
fun printSomething() {
println(something)
}
}
class Child(override val something: String): Base()
fun main() {
Child("Test").printSomething() // correctly prints "Test"
}
编辑:以下是关于评论中后续问题的一些说明。
我不太明白为什么 init 块中的代码用于子类构造函数中的参数
我认为您可能会对 Kotlin 的主要构造函数的简洁语法感到困惑,这可能会使调试器的流程难以理解。在Child
声明中,我们其实声明了很多东西:
- 传递给的主构造函数的参数
something
Child
- 类的属性
something
,Child
它覆盖了父类的属性 - 对父构造函数 (
Base()
)的调用
当Child()
被调用时,它立即调用运行该块的Base()
无参数构造函数。init
我们甚至没有用参数或任何东西委托基本构造函数,但它仍然用于执行覆盖的参数
您可能会在这里混合声明和运行时。虽然我们在类和类中声明了东西,但在这个示例代码中,运行时只有 1 个实例(的实例)。Base
Child
Child
所以,实际上,这里只调用了 1 个属性something
(内存中只有一个位置)。如果init
块访问这个属性,它只能是子实例的属性。我们不需要向Base
构造函数传递任何内容,因为该块是使用实例init
的数据/字段有效执行的。Child
如果您看到与此等效的 Java,也许您会不那么困惑。如果您将 abstractsomething
视为 getter 的声明,那就很明显了getSomething()
。子类重写此getSomething()
方法并声明一个私有something
字段,getter 返回该字段的当前值something
。但是该字段仅在父(和 init 块)的构造函数完成执行后才被初始化。
推荐阅读
- python - 合并图像直方图的相似分箱
- javascript - 使用 formData 通过 ajax 发布时,$_POST 保持为空
- javascript - 类型错误:firebase__WEBPACK_IMPORTED_MODULE_2__.firestore 不是函数
- symfony - 添加自定义 Symfony 通知程序通道
- druid - 我可以加入两个数据源并在德鲁伊中永久创建一个新的数据源吗
- gitlab - 使用 Yarn PNP 构建部署时以 PM2 集群模式运行 NestJS
- python - 如何使用@abstractmethod 创建一个抽象接口,指定其构造函数的参数结构?
- excel - 将值粘贴到excel VBA中的过滤表中
- c# - Facebook c# sdk 错误 OAuthException - #2
- android-library - 将独立 Android 库发布到 GitHub 包的标准方法是什么