我试图理解为什么以下代码抛出:
open class Base(open val input: String) { lateinit var derived: String init { derived = input.toUpperCase() // throws! } } class Sub(override val input: String) : Base(input)
在调用此代码时,如下所示:
println(Sub("test").derived)
它抛出一个异常,因为当时toUpperCase
被调用,input
解析为null
.我发现这个反直觉:我将一个非null值传递给主构造函数,但是在超类的init块中它解析为null?
我想我对可能发生的事情有一个模糊的概念:因为input
既可以作为构造函数参数也可以作为属性,赋值内部调用this.input
,但this
尚未完全初始化.这真的很奇怪:在IntelliJ调试器中,input
正常解析(到值"test"),但是一旦我调用表达式评估窗口并input
手动检查,它就会突然变为空.
假设这是预期的行为,你建议做什么,即当需要初始化从同一类的属性派生的字段时?
更新: 我发布了两个更简洁的代码片段,说明了混淆源自何处:
https://gist.github.com/mttkay/9fbb0ddf72f471465afc https://gist.github.com/mttkay/5dc9bde1006b70e1e8ba
原始示例等效于以下Java程序:
class Base { private String input; private String derived; Base(String input) { this.input = input; this.derived = getInput().toUpperCase(); // Initializes derived by calling an overridden method } public String getInput() { return input; } } class Derived extends Base { private String input; public Derived(String input) { super(input); // Calls the superclass constructor, which tries to initialize derived this.input = input; // Initializes the subclass field } @Override public String getInput() { return input; // Returns the value of the subclass field } }
在Sub类中重写了getInput()方法,因此代码调用Sub.getInput().此时,Sub类的构造函数尚未执行,因此保存Sub.input值的后备字段仍为null.这不是Kotlin的错误; 您可以轻松地在纯Java代码中遇到同样的问题.
修复是不覆盖属性.(我已经看过你的评论,但这并没有真正解释为什么你认为你需要覆盖它.)