引用子类中定义的val时出现NullPointerException

时间:2017-12-08 12:58:09

标签: scala

我有一个扩展抽象类的case类。在其主构造函数中,超类引用在子类中定义的抽象值。

abstract class SuperClass {
    def abstractValue: Seq[Int]

    val concreteValue: Int = {
        abstractValue.head
    }
}

case class SubClass() extends SuperClass {
    override val abstractValue: Seq[Int] = Seq(1,2,3)
}

我期望创建SubClass的实例会导致对象abstractValue = Seq(1,2,3)concreteValue = 1。但是,创建SubClass的实例会引发NullPointerException,因为Scala会尝试在concreteValue之前初始化abstractValue,这意味着后者在null时为abstractValue已初始化。

如何使这项工作?我试过的一些事情:

  • SubClassval的声明从def更改为abstractValue可以解决问题,但这会导致abstractValue被重新初始化的意外后果每次引用它。这可能在示例中没问题,但在以下情况下不是这样:
    • abstractValue的初始化很昂贵。
    • concreteValue的初始化不可重复(我的用例)。
  • SuperClassval的声明从def更改为abstractValue同样如此。
  • SuperClassdef的声明从val更改为var并不能解决问题。
  • 可能使用val代替abstractValue来解决这个问题,但我喜欢我的案例类实例不可变,非常感谢。

1 个答案:

答案 0 :(得分:2)

我发现满足问题中提到的所有限制的唯一解决方案是将override lazy val abstractValue: Seq[Int] = Seq(1,2,3) 的初始化更改为惰性:

abstractValue

这违反直觉导致concreteValue的初始化早于发生。具体来说,它发生在“需要时”,即concreteValue初始化期间。

编辑:正如Shane Perry在评论中所说,将abstractValue定义为懒惰(而不是Document doc = null; try { doc = Jsoup.connect("http://en.wikipedia.org/").get(); } catch (IOException e) { e.printStackTrace(); } )也解决了这个问题。这种方法更直观,更易于维护。