Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialisation order throws null pointer on lazy val access

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?

like image 625
Mario Galic Avatar asked Feb 26 '26 21:02

Mario Galic


1 Answers

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
   }

}
like image 76
Bogdan Vakulenko Avatar answered Mar 01 '26 20:03

Bogdan Vakulenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!