2

以下代码,在 REPL 中输入时

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

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

new B()

给出一个空点异常,而不是一个不变的失败。

解决这个问题的最佳成语是什么?


类似的问题:

代码契约:抽象类中的不变量

抽象类Scala中的私有构造函数?

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

4

1 回答 1

2

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

  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_A为和分配内存aSet_B并设置为null
  2. 的初始化A程序正在运行。
  3. requireonaSet.contains(3)被调用
  4. 由于aSet在 中被覆盖B,它返回aSet_B
  5. 因为aSet_Bis null,所以抛出一个 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 常见问题解答的必填链接:

相关问题列表:

  1. 为什么使用val实现抽象方法并在val表达式中从超类调用返回NullPointerException
  2. 运行覆盖值的父代码,但未在父处分配值
于 2018-08-08T12:26:51.827 回答