用val重写def的Scala抛出NPE

时间:2016-05-27 14:02:45

标签: scala

我正在学习ScalaProgramming in Scala 3rd EdCh 10, Page 225Overriding methods and fields部分,它说

  

统一访问原则只​​是Scala处理的一个方面   字段和方法比Java更统一。另一个区别是   在Scala中,字段和方法属于同一名称空间。这个   使字段可以覆盖无参数方法。对于   例如,您可以更改类中内容的实现   ArrayElement从一个方法到一个字段而不必修改   类Element中内容的抽象方法定义,如图所示   代码清单10.4:

基于该示例的代码是

使用def

abstract class Element {
  def contents: Array[String]

  val height = contents.length

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


class ArrayElement(contnts: Array[String]) extends Element {
  def contents: Array[String] = contnts
}

// --
val ae = new ArrayElement(Array("hello", "world"))
ae.height
ae.width

我得到了

ae: ArrayElement = ArrayElement@7cd3ba8e
res0: Int = 2
res1: Int = 5

将def覆盖为ArrayElement中的val

abstract class Element {
  def contents: Array[String]

  val height = contents.length

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


class ArrayElement(contnts: Array[String]) extends Element {
  val contents: Array[String] = contnts
}

// --
val ae = new ArrayElement(Array("hello", "world"))
ae.height
ae.width

我将NPE视为

java.lang.NullPointerException
    at #worksheet#.Element.<init>(scratch.scala:4)
    at #worksheet#.ArrayElement.<init>(scratch.scala:10)
    at #worksheet#.ae$lzycompute(scratch.scala:15)
    at #worksheet#.ae(scratch.scala:15)
    at #worksheet#.#worksheet#(scratch.scala:14)

我错过了什么?

1 个答案:

答案 0 :(得分:8)

类级别字段在其他任何内容之前初始化,这意味着已分配null。您可以将声明设为lazy val,并且在调用之前不会对其进行初始化。这就是def工作的原因。但是,更好的方法是,不要创建隐藏私有构造函数字段的类公共字段,而是将构造函数字段设置为public,如下所示:

class ArrayElement(val contnts: Array[String]) extends Element {}

由于这里也有一个父类,所以将它标记为重写是好的;

class ArrayElement(override val contnts: Array[String]) extends Element {}

如果这将成为无状态数据容器类,最好的选择是使其成为case class,其中(在其他几个方面)具有默认的公共字段。

case class ArrayElement(override val contnts: Array[String]) extends Element

这是更惯用的scala,它将为您提供基于值的equalshashCode,模式匹配,更简单的构造(无需new