我正在使用的类比这要复杂得多,但我认为这个任意示例同样适用。假设我有一个基础抽象类,它总是想将其Int成员之一foo乘以2。只要它是Int,它实际上并不关心值是什么。另外,假设我们不关心何时发生乘法,只要它发生在构造函数时间附近即可。最后,不允许将foo作为构造函数参数传递。它必须是被子类覆盖的类成员。
abstract class ExampleClass {
abstract val foo: Int
init {
println("Here is foo times 2!: ${foo * 2}")
}
}
class ChildClass : ExampleClass() {
override val foo = 5
}
这显然行不通。父类首先被构造,因此在运行时将引发错误,因为该成员尚未实例化。但是根据我的条件,我不介意在子类实例化之后是否进行foo的工作。 父类是否有办法说“我的孩子实例化完成后就做”?还是我只需要让步:
abstract class ExampleClass {
abstract val foo: Int
fun bar() {
println("Here is foo times 2!: ${foo * 2}")
}
}
class ChildClass : ExampleClass() {
override val foo = 5
init {
bar()
}
}
我的所有子类都在初始化时调用bar()
吗?用这种方式这样做是错误的,因为用foo完成的工作只会发生一次。
答案 0 :(得分:2)
我相信答案是否定的,这是不可能的。
科特林语言documentation for class initialization这样说:
[B]在执行基类构造函数时,尚未初始化在派生类中声明或重写的属性。如果在基类初始化逻辑中使用了这些属性中的任何一个(直接或间接地,通过另一个重写的开放成员实现),则可能导致错误的行为或运行时失败。因此,在设计基类时,应避免在构造函数,属性初始化程序和init块中使用开放成员。
abstract
属性本质上是open
,因此这里的建议也适用于您的情况:设计类以避免这种情况。
此外,我找不到任何回调样式方法或其他任何方法来“钩住”类初始化逻辑/流并说“初始化完成后执行foo()
”。
答案 1 :(得分:0)