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
}
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;
()
}
};
However, why is null pointer also thrown when x
is lazy like so
object Bar extends Foo {
lazy val x = 42
}
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>();
()
}
};
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;
Runtime field accessors check -Xcheckinit
seems to be satisfied on my machine with Scala 2.12.8, so why NullPointerException
when lazy val x
?