'this'在此上下文中未定义

时间:2018-01-25 20:29:22

标签: kotlin

如何解决以下情况?

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友好的语言来实现呢?

2 个答案:

答案 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语言的优良传统,例如JavaC#Swift,Kotlin不允许您在调用超类之前泄漏this引用初始化已完成。在您的特殊情况下,您只是存储引用,但在稍微不同的情况下,超类代码可能会尝试使用接收到的对象,此时该对象仍然未初始化。

作为语言不允许这样做的具体示例,请考虑A是您使用的库中的类且此规则无效的情况。你像你一样通过this,事情很好。稍后您将库更新为更新版本,并且它恰好将i.toString()作为良性添加到其构造函数中。它不知道它实际上是在调用一个被重写的方法。您的toString()实施会观察其所有不变量,例如未初始化的val

这种设计遇到了其他问题,而不仅仅是你现在正在努力解决的循环初始化依赖问题。简而言之,课程A期望:

A and I

但你创建了这个:

C is I

A依赖于I类型的协作者对象。它不希望自己成为合作者。这可能会带来各种奇怪的错误。例如,您的C.toString()可以委托给super.toString()A.toString() A super C可以调用I.toString() ,产生StackOverflowError

我无法从您的问题中说明A是否设计用于扩展,这会使C : A部分正确无误,但您绝对应该将AI区分开来