预期,没有惰性val的以下初始化顺序将引发空指针异常
class Foo {
Bar.x // NullPointerException
}
object Bar extends Foo {
val x = 42
}
object Hello extends App {
Bar
}
检查-Xprint:jvm
的输出并引用@paradigmatic answer,我们看到这是由于Foo
的构造函数首先运行并在Bar.x()
之前调用Bar.this.x
在Bar
的构造函数中初始化:
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
是lazy时也会抛出空指针
object Bar extends Foo {
lazy val x = 42
}
在有惰性情况下分析-Xprint:jvm
输出
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
后卫来工作
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap$0.unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
在运行Scala 2.12.8的计算机上,运行时字段访问器检查-Xcheckinit
似乎很满意,那么为什么NullPointerException
时lazy val x
是
答案 0 :(得分:4)
我认为该NPE与val
根本无关。检查一下:
class Foo {
Bar.anyMethod
}
object Bar extends Foo {
def anyMethod = ???
}
object Hello extends App {
Bar
}
//java.lang.NullPointerException
Foo
试图在Bar
上运行时在Bar
上运行构造函数。因此,这就是您的Foo
在致电x
之前所做的事情。
如果您使用Hello
方法将所有内容放入main
,则无论是我还是您的案件,您都将获得StackOverflow而不是NPE。
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
}
}