抽象超类Scala中的断言创建NPE

时间:2018-08-08 11:41:37

标签: scala require abstract object-initialization

在REPL中输入以下代码

abstract class A { val aSet: Set[Int]; require(aSet.contains(3)) }

class B extends A { val aSet = Set(4,5,6) }

new B()

给出空值异常,而不是不变的失败。

解决这个问题的最佳习惯是什么?


类似的问题:

Code Contracts: Invariants in abstract class

Private constructor in abstract class Scala?

以及在线评论:https://gist.github.com/jkpl/4932e8730c1810261381851b13dfd29d

1 个答案:

答案 0 :(得分:2)

当您声明val时,会发生几件事:

  1. 编译器确保在初始化类的实例时为变量分配足够的空间
  2. 已创建访问方法
  3. 创建用于设置变量初始值的初始化程序。

您的代码

abstract class A { val aSet: Set[Int]; require(aSet.contains(3)) }
class B extends A { val aSet = Set(4,5,6) }
new B()

大致等同于

abstract class A { 
  private var aSet_A: Set[Int] = null
  def aSet: Set[Int] = aSet_A
  require(aSet.contains(3)) 
}

class B extends A {
  private var aSet_B: Set[Int] = Set(4,5,6) 
  override def aSet: Set[Int] = aSet_B
}

new B()

因此,会发生以下情况:

  1. 已分配aSet_AaSet_B的内存并将其设置为null
  2. A的初始化程序已运行。
  3. 调用了require上的
  4. aSet.contains(3)
  5. 由于aSet中的B被覆盖,因此它返回aSet_B
  6. 由于aSet_Bnull,因此会引发NPE。

为避免这种情况,您可以将aSet实现为惰性变量:

abstract class A { 
  def aSet: Set[Int]
  require(aSet.contains(3)) 
}

class B extends A {
  lazy val aSet = Set(4,5,6) 
}

new B()

这会引发requirement failed异常:

java.lang.IllegalArgumentException: requirement failed

Scala FAQ的必填链接:

相关问题列表:

  1. Why is my abstract or overridden val null?
  2. Why does implement abstract method using val and call from superclass in val expression return NullPointerException