3

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?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98

1 Answers1

4

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
   }

}
Bogdan Vakulenko
  • 3,380
  • 1
  • 10
  • 25
  • I cannot seem to replicate `StackOverflowError` in either case when putting everything in `Hello`, however it does throw NPE. – Mario Galic May 10 '19 at 13:46
  • `StackOverflowError` is thrown only with `main` method. fixed that. – Bogdan Vakulenko May 10 '19 at 13:52
  • Seems strange calling child's constructor from parent's constructors cycle is legal. NPE being thrown in the first place instead of `StackOverflowError` is also confusing. – Mario Galic May 10 '19 at 14:43
  • 2
    `NPE` instead of `StackOverflow` in first place is somehow related to the fact that there is no stack available at this point. Stack is available only inside methods. – Bogdan Vakulenko May 10 '19 at 14:51