初始化顺序在惰性val访问上抛出空指针 [英] Initialisation order throws null pointer on lazy val access
问题描述
预期,没有惰性val的以下初始化顺序将引发空指针异常
Expectedly, the following initialisation order without lazy val throws null pointer exception
class Foo {
Bar.x // NullPointerException
}
object Bar extends Foo {
val x = 42
}
object Hello extends App {
Bar
}
检查-Xprint:jvm
的输出,并引用@paradigmatic 答案,我们看到这是由于Foo
的原因构造函数首先运行,然后在Bar
的构造函数中初始化Bar.this.x
之前调用Bar.x()
:
Examining -Xprint:jvm
output, and referencing @paradigmatic answer, we see this is due to Foo
's constructor running first and calling Bar.x()
before Bar.this.x
is initialised in Bar
's constructor:
class Foo extends Object {
def <init>(): example.Foo = {
Foo.super.<init>();
Bar.x();
()
}
};
object Bar extends example.Foo {
private[this] val x: Int = _;
<stable> <accessor> def x(): Int = Bar.this.x;
def <init>(): example.Bar.type = {
Bar.super.<init>();
Bar.this.x = 42;
()
}
};
但是,为什么x
为懒惰像这样
object Bar extends Foo {
lazy val x = 42
}
在有延迟的情况下分析-Xprint:jvm
输出
Analysing -Xprint:jvm
output in lazy case we have
class Foo extends Object {
def <init>(): example.Foo = {
Foo.super.<init>();
Bar.x();
()
}
};
object Bar extends example.Foo {
final <synthetic> lazy private[this] var x: Int = _;
@volatile private[this] var bitmap$0: Boolean = _;
private def x$lzycompute(): Int = {
Bar.this.synchronized(if (Bar.this.bitmap$0.unary_!())
{
Bar.this.x = (42: Int);
Bar.this.bitmap$0 = true
});
Bar.this.x
};
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap$0.unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
def <init>(): example.Bar.type = {
Bar.super.<init>();
()
}
};
在我看来应该可以使用bitmap$0
防护罩
where it seems to me it should work due to the bitmap$0
guard
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap$0.unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
运行时字段访问器检查-Xcheckinit
在使用Scala 2.12.8的计算机上似乎很满意,那么为什么NullPointerException
当lazy val x
呢?
Runtime field accessors check -Xcheckinit
seems to be satisfied on my machine with Scala 2.12.8, so why NullPointerException
when lazy val x
?
推荐答案
我认为该NPE与val
根本无关.检查一下:
I don't think this NPE is related to val
at all. Check this:
class Foo {
Bar.anyMethod
}
object Bar extends Foo {
def anyMethod = ???
}
object Hello extends App {
Bar
}
//java.lang.NullPointerException
Foo
试图在Bar
仍在构建时在Bar
上运行构造函数.因此,这就是Foo
在调用x
之前所做的事情.
Foo
is trying to run constructor on Bar
while Bar
is still under construction. So that's what your Foo
is doing too before calling x
.
顺便说一句,如果您使用main
方法将所有内容放入Hello
,则无论是我还是您的案件,都会得到StackOverflow而不是NPE.
Btw if you put everything into Hello
with main
method you will get StackOverflow instead of NPE in both mine and your cases.
object Hello {
def main(args: Array[String]): Unit = {
class Foo {
Bar.anyMethod
}
object Bar extends Foo { //<- Bar is like local val now instead of field
def anyMethod= ??? // of package object, so stack is available now.
}
Bar
}
}
这篇关于初始化顺序在惰性val访问上抛出空指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!