用val重写def的Scala抛出NPE

day*_*mer 6 scala

我学习ScalaProgramming in Scala 3rd Ed,Ch 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
Run Code Online (Sandbox Code Playgroud)

我明白了

ae: ArrayElement = ArrayElement@7cd3ba8e
res0: Int = 2
res1: Int = 5
Run Code Online (Sandbox Code Playgroud)

将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
Run Code Online (Sandbox Code Playgroud)

我知道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)
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

Dae*_*yth 8

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

class ArrayElement(val contnts: Array[String]) extends Element {}
Run Code Online (Sandbox Code Playgroud)

由于这里也有一个家长班,所以最好将它标记为重写;

class ArrayElement(override val contnts: Array[String]) extends Element {}
Run Code Online (Sandbox Code Playgroud)

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

case class ArrayElement(override val contnts: Array[String]) extends Element
Run Code Online (Sandbox Code Playgroud)

这是更为惯用的Scala,它会为你提供一个基于价值的equals,hashCode模式匹配,结构更简单(无需new)

  • 真正。一个简单的经验法则是永远不要在val中引用抽象的def,因为不能保证在实例化val时def将具有任何值。@Daenyth,我编辑了您的评论以提供其他选择 (2认同)