初始化顺序在惰性val访问上抛出空指针 [英] Initialisation order throws null pointer on lazy val access

查看:45
本文介绍了初始化顺序在惰性val访问上抛出空指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

预期,没有惰性val的以下初始化顺序将引发空指针异常

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
}

检查-Xprint:jvm的输出,并引用@paradigmatic 答案,我们看到这是由于Foo的原因构造函数首先运行,然后在Bar的构造函数中初始化Bar.this.x之前调用Bar.x():

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;
      ()
    }
  };

但是,为什么x懒惰像这样

object Bar extends Foo {
  lazy val x = 42
}

在有延迟的情况下分析-Xprint:jvm输出

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>();
      ()
    }
  };

在我看来应该可以使用bitmap$0防护罩

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;

运行时字段访问器检查-Xcheckinit在使用Scala 2.12.8的计算机上似乎很满意,那么为什么NullPointerExceptionlazy val 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?

推荐答案

我认为该NPE与val根本无关.检查一下:

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试图在Bar仍在构建时在Bar上运行构造函数.因此,这就是Foo在调用x之前所做的事情.

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.

顺便说一句,如果您使用main方法将所有内容放入Hello,则无论是我还是您的案件,都会得到StackOverflow而不是NPE.

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
   }

}

这篇关于初始化顺序在惰性val访问上抛出空指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆