如何解决以下情况?
interface I
class A(i: I)
class C : I, A(this) // << --- 'this' is not defined in this context
简而言之,我想将类实例传递给超类构造函数 在Kotlin有可能吗?
P.S。 所有答案都很好,技术上也是正确的。但让我们举一个具体的例子:
interface Pilot {
fun informAboutObstacle()
}
abstract class Car(private val pilot: Pilot) {
fun drive() {
while (true) {
// ....
if (haveObstacleDetected()) {
pilot.informAboutObstacle()
}
// ....
}
}
fun break() {
// stop the car
}
}
class AutopilotCar : Pilot, Car(this) { // For example, Tesla :)
override fun informAboutObstacle() {
break() // stop the car
}
}
这个例子看起来不太费力,为什么我不能用OOP友好的语言来实现呢?
答案 0 :(得分:6)
不,这在JVM上是不可能的。 this
仅在超类初始化后才可用。
这
https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.10.2.4
类myClass的实例初始化方法(第2.9.1节)将新的未初始化对象视为本地变量0中的此参数。在该方法调用myClass的另一个实例初始化方法或其直接超类之前,唯一的操作该方法可以对此进行分配在myClass中声明的字段。
因此,在调用超类构造函数之前,禁止在堆栈上推送aload 0
的字节码指令this
。这就是为什么它不能作为参数传递给超级构造函数。
Kotlin是作为JVM语言诞生的,旨在实现与Java代码的最大互操作性以及其语言功能的最小开销。虽然Kotlin可能选择以不同的方式编排对象初始化,但它会在混合的Java-Kotlin类层次结构中产生问题并增加显着的开销。
答案 1 :(得分:2)
根据OOP语言的优良传统,例如Java,C#或Swift,Kotlin不允许您在调用超类之前泄漏this
引用初始化已完成。在您的特殊情况下,您只是存储引用,但在稍微不同的情况下,超类代码可能会尝试使用接收到的对象,此时该对象仍然未初始化。
作为语言不允许这样做的具体示例,请考虑A
是您使用的库中的类且此规则无效的情况。你像你一样通过this
,事情很好。稍后您将库更新为更新版本,并且它恰好将i.toString()
作为良性添加到其构造函数中。它不知道它实际上是在调用一个被重写的方法。您的toString()
实施会观察其所有不变量,例如未初始化的val
。
这种设计遇到了其他问题,而不仅仅是你现在正在努力解决的循环初始化依赖问题。简而言之,课程A
期望:
但你创建了这个:
类A
依赖于I
类型的协作者对象。它不希望自己成为合作者。这可能会带来各种奇怪的错误。例如,您的C.toString()
可以委托给super.toString()
而A.toString()
A
super
C
可以调用I.toString()
,产生StackOverflowError
。
我无法从您的问题中说明A
是否设计用于扩展,这会使C : A
部分正确无误,但您绝对应该将A
与I
区分开来