scala中一个奇怪的NullPointerException

scala中一个奇怪的NullPointerException,scala,Scala,代码如下: abstract class Element { def contents: Array[String] val height = contents.length val width = if(height ==0 ) 0 else contents(0).length override def toString = contents(0) } class ArrayElement(override val contents: Array[String]) exte

代码如下:

abstract class Element {
  def contents: Array[String]
  val height = contents.length
  val width = if(height ==0 ) 0 else contents(0).length

  override def toString = contents(0)
}
class ArrayElement(override val contents: Array[String]) extends Element

class LineElementT(s: String) extends Element {
  override def contents = Array(s)
}

class LineElementF(s: String) extends Element {
  override val contents = Array(s)
}

这三个子类是正常的,除了
LineElementF
,它在使用
val lef=newlineelementf(“错误”)

创建实例时抛出一个
NullPointerException
,此NPE发生在高度初始化时。高度初始化时,LineElementF的val内容未初始化

通过将高度和宽度都声明为惰性,可以避免此NPE:

lazy val height = contents.length
lazy val width = if(height ==0 ) 0 else contents(0).length

Scala语言还有一种特殊的语法(有时称为),它允许在其超类的构造函数之前执行子类的构造函数:

class LineElementEI(s: String) extends {
  override val contents = Array(s)
} with Element

要澄清为什么常规的“延迟初始化”不适用于您的
LineElementF
:在超类的主体中,您试图计算
内容。length
,但子类的主体仅在超类之后计算,因此
内容
仍然是
null
。您可以通过对依赖于子类初始化的所有字段使用惰性求值来解决此问题,也可以如上所示更改初始化顺序。

谢谢,但您能否告诉我为什么其他两个子类工作得很好?区别在于,您将
内容
定义为
val
def。
val`的右侧部分在构造时进行评估,而
def
的右侧部分在调用时进行评估。您可以将内容声明为
lazy val contents
,以便在第一次调用时仅对其求值一次。