初始化顺序在延迟 val 访问时抛出空指针
Initialisation order throws null pointer on lazy val access
不出所料,以下没有lazy 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[=13=]: Boolean = _;
private def x$lzycompute(): Int = {
Bar.this.synchronized(if (Bar.this.bitmap[=13=].unary_!())
{
Bar.this.x = (42: Int);
Bar.this.bitmap[=13=] = true
});
Bar.this.x
};
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap[=13=].unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
def <init>(): example.Bar.type = {
Bar.super.<init>();
()
}
};
在我看来,由于 bitmap[=22=]
守卫
,它应该可以工作
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap[=14=].unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
运行时字段访问器检查 -Xcheckinit
在我的机器上使用 Scala 2.12.8 似乎很满意,所以为什么 NullPointerException
而 lazy val x
?
我认为这个 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
之前所做的事情。
顺便说一句,如果您使用 main
方法将所有内容都放入 Hello
中,您将获得 Whosebug 而不是我和您的案例中的 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
}
}
不出所料,以下没有lazy 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[=13=]: Boolean = _;
private def x$lzycompute(): Int = {
Bar.this.synchronized(if (Bar.this.bitmap[=13=].unary_!())
{
Bar.this.x = (42: Int);
Bar.this.bitmap[=13=] = true
});
Bar.this.x
};
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap[=13=].unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
def <init>(): example.Bar.type = {
Bar.super.<init>();
()
}
};
在我看来,由于 bitmap[=22=]
守卫
<stable> <accessor> lazy def x(): Int = if (Bar.this.bitmap[=14=].unary_!())
Bar.this.x$lzycompute()
else
Bar.this.x;
运行时字段访问器检查 -Xcheckinit
在我的机器上使用 Scala 2.12.8 似乎很满意,所以为什么 NullPointerException
而 lazy val x
?
我认为这个 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
之前所做的事情。
顺便说一句,如果您使用 main
方法将所有内容都放入 Hello
中,您将获得 Whosebug 而不是我和您的案例中的 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
}
}