Kotlin 从内部类的构造函数访问外部类抛出 NPE,为什么?

问题描述 投票:0回答:1

Kotlin 从内部类的构造函数访问外部类抛出 NPE

这段代码按预期工作: https://pl.kotl.in/xaSu-xe9u

interface M {
    fun hello()
}

abstract class Parent : M {
    init {
        this.hello()
    }
}

class Outer : M {
    override fun hello() {
        println("hello, world")
    }

    inner class Inner : Parent(), M by this {
        override fun hello() {
            [email protected]()
        }
    }
}

fun main() {
    val out = Outer()
    val inner = out.Inner()
}

但是下面的代码会抛出 NPE: https://pl.kotl.in/PfogCZBSW

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Outer.hello()" because "this.$$delegate_0" is null
    at Outer$Inner.hello(Bug.kt)
    at Parent.<init>(Bug.kt:9)
    at Outer$Inner.<init>(Bug.kt:18)
    at BugKt.main(Bug.kt:23)
    at BugKt.main(Bug.kt)
interface M {
    fun hello()
}

abstract class Parent : M {
    init {
        this.hello()
    }
}

class Outer : M {
    override fun hello() {
        println("hello, world")
    }

    inner class Inner : Parent(), M by this
}

fun main() {
    val out = Outer()
    val inner = out.Inner()
}

为什么?这是编译器错误吗?

kotlin
1个回答
0
投票

我们可能会争论这是否是一个编译器错误,但我认为像这样搞乱对象初始化是自找麻烦。对我来说,在构造函数中调用抽象函数本身就是一个危险信号。您使用多种高级功能(例如委派),并希望一切都能按照您需要的顺序进行。它可能有效也可能无效。

我们可以在生成的字节码中找到准确的解释:

   L0
    LINENUMBER 22 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD Outer$Inner.this$0 : LOuter;
    ALOAD 0
   L1
    LINENUMBER 22 L1
    INVOKESPECIAL Parent.<init> ()V
    ALOAD 0
    ALOAD 1
    PUTFIELD Outer$Inner.$$delegate_0 : LOuter;
    RETURN

首先,它设置

Outer
,然后调用超级构造函数,然后才设置
Outer
委托。如果我们意识到父构造函数通常在设置子类声明的字段之前执行,这是有道理的。它也符合我们在源代码中看到的顺序:
Parent(), M by this

© www.soinside.com 2019 - 2024. All rights reserved.