Ожидается, что следующий порядок инициализации без ленивого 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
ленивый , как это
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;
Проверка доступа к полевым полям во время выполнения -Xcheckinit
, кажется, удовлетворяет на моей машине с Scala 2.12.8, так почему NullPointerException
когда lazy val x
?