Initialisation order throws null pointer on lazy val access

纵然是瞬间 提交于 2020-01-02 08:08:06

问题


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?


回答1:


I don't think this NPE is related to val at all. Check this:

class Foo {
  Bar.anyMethod
}

object Bar extends Foo {
  def anyMethod = ???
}

object Hello extends App {
  Bar
}

//java.lang.NullPointerException

Foo is trying to run constructor on Bar while Bar is still under construction. So that's what your Foo is doing too before calling x.

Btw if you put everything into Hello with main method you will get StackOverflow instead of NPE in both mine and your cases.

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
   }

}


来源:https://stackoverflow.com/questions/56077401/initialisation-order-throws-null-pointer-on-lazy-val-access

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!