初始化顺序在惰性 val 访问上抛出空指针

2024-02-10

预期,以下没有惰性 val 的初始化顺序会抛出空指针异常

class Foo {
  Bar.x // NullPointerException
}

object Bar extends Foo {
  val x = 42
}

object Hello extends App {
  Bar
}

正在检查-Xprint:jvm输出,并引用@paradigmaticanswer https://stackoverflow.com/a/12185848/5205022,我们看到这是由于Foo的构造函数首先运行并调用Bar.x() before 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 is lazy https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html#use-lazy-vals like so

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 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 when 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 while Bar仍在建设中。所以这就是你的Foo在打电话之前也在做x.

顺便说一句,如果你把一切都投入Hello with 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
   }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

初始化顺序在惰性 val 访问上抛出空指针 的相关文章

随机推荐