Assertions in abstract superclass scala creating N

2019-02-26 05:58发布

The following code, when entered in REPL

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

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

new B()

gives a null point exception, rather than an invariant failure.

What would be the best idiom to solve this problem?


Similar questions:

Code Contracts: Invariants in abstract class

Private constructor in abstract class Scala?

and also online commentary: https://gist.github.com/jkpl/4932e8730c1810261381851b13dfd29d

1条回答
仙女界的扛把子
2楼-- · 2019-02-26 06:24

When you declare a val, several things happen:

  1. The compiler makes sure that enough space is allocated for the variable when an instance of the class is initialized
  2. An accessor-method is created
  3. Initializers that setup the initial values of the variables are created.

Your code

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

is roughly equivalent to

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

so, the following happens:

  1. Memory for aSet_A and aSet_B is allocated and set to null.
  2. Initializer of A is run.
  3. require on aSet.contains(3) is invoked
  4. Since aSet is overridden in B, it returns aSet_B.
  5. Since aSet_B is null, an NPE is thrown.

To avoid this, you can implement aSet as a lazy variable:

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

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

new B()

This throws the requirement failed exception:

java.lang.IllegalArgumentException: requirement failed

Obligatory link to Scala's FAQ:

List of related questions:

  1. Why does implement abstract method using val and call from superclass in val expression return NullPointerException
  2. Overridden value parent code is run but value is not assigned at parent
查看更多
登录 后发表回答