【问题标题】:'this' is not defined in this context'this' 未在此上下文中定义
【发布时间】:2018-01-29 05:50:35
【问题描述】:

我该如何解决以下情况?

interface I
class A(i: I)
class C : I, A(this) // << --- 'this' is not defined in this context

简而言之,我想将类实例传递给超类构造函数。
在 Kotlin 中可以吗?

附言 所有的答案都是好的并且在技术上是正确的。但是让我们举一个具体的例子:

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

【问题讨论】:

  • 这可能比继承更适合组合。让您的C 维护I 的实例,而不是成为 一个。或者,您可以允许 A 类通过 setter 提供其 I,而不是在初始化时提供。
  • 如果它必须是继承,您可以使用辅助零参数构造函数并检查它是否实现了 I?
  • 我认为您的扩展示例并没有为问题陈述添加任何实质性内容。你仍然有这个不方便的事实,你让Car 引用自己,就好像它是另一个合作者对象一样。我认为将Autopilot 建模为PilotCar 没有任何好处。

标签: kotlin


【解决方案1】:

不,这在 JVM 上是不可能的。 this 仅在超类初始化后可用。

来自

https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.10.2.4

类 myClass 的实例初始化方法(第 2.9.1 节)将新的未初始化对象视为其在局部变量 0 中的 this 参数。在该方法调用 myClass 或其直接超类的另一个实例初始化方法之前,唯一的操作可以对此执行的方法是分配在 myClass 中声明的字段。

因此,在调用超类构造函数之前,禁止使用字节码指令aload 0this 压入堆栈。这就是为什么它不能作为参数传递给超级构造函数的原因。

Kotlin 是作为一种 JVM 语言诞生的,旨在最大限度地实现与 Java 代码的互操作性,并将其语言特性的开销降至最低。虽然 Kotlin 可以选择以不同的方式编排对象初始化,但它会在混合 Java-Kotlin 类层次结构中产生问题并增加大量开销。

【讨论】:

    【解决方案2】:

    JavaC#Swift 等 OOP 语言的良好传统中,Kotlin 不允许您在调用超类初始化完成之前泄露 this 引用。在您的特殊情况下,您只是存储引用,但在稍微不同的情况下,超类代码可能会尝试使用接收到的对象,此时该对象仍未初始化。

    作为语言不允许这样做的具体示例,请考虑A 是您使用的库中的一个类并且此规则无效的情况。你像你一样通过this,一切正常。稍后您将库更新到较新的版本,它恰好在其构造函数中添加了像i.toString() 这样良性的东西。它不知道它实际上是在自己调用一个被覆盖的方法。您的 toString() 实现观察到其所有不变量均已损坏,例如未初始化的 vals。

    这种设计还存在其他问题,而不仅仅是您现在正在努力解决的循环初始化依赖性。简而言之,A 类期望这样:

    但是你创建了这个:

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

    我不能从你的问题中说A 是否是为扩展而设计的,这会使C : A 部分正确,但你绝对应该将AI 分开。

    【讨论】:

      猜你喜欢
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多