当前位置: 首页 > 知识库问答 >
问题:

Scala用val覆盖def抛出NPE

能向晨
2023-03-14

我正在学习Scala和在Scala第3版编程第10章,第225页,部分覆盖方法和字段,它说

统一访问原则只是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

在ArrayElement中将def重写为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个答案

夏侯元忠
2023-03-14

类级字段在任何其他字段之前初始化,这意味着分配了null。您可以将声明设为惰性val,并且在调用之前不会初始化它。这就是def工作的原因。但是,一种更好的方法不是创建一个隐藏私有构造函数字段的类公共字段,而是将构造函数字段公开,如下所示:

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

因为这里也有父类在起作用,所以最好将其标记为重写;

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

如果这将是一个无状态数据容器类,那么最好的选择是将其设置为一个case类,该类(除其他外)默认具有公共字段。

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

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

 类似资料:
  • 我浏览了有效的scala幻灯片,在幻灯片10中提到,不要在抽象成员的中使用,而是使用。幻灯片没有详细提到为什么在中使用抽象是反模式。如果有人能解释在抽象方法的特征中使用val vs def的最佳实践,我将不胜感激

  • 我引用马丁·奥德斯基的话: def表单是“按名称”的,每次使用时都会对其右手边进行评估。 val定义的右侧是在定义本身处计算的。 null null 我有点困惑为什么:

  • 我最近在我的Eclipse设置中做了一些改动,主要是我将Java版本从8升级到了15,我花了一段时间才注意到,但是我不能再对我的任何项目进行覆盖测试了,因为它抛出了一个Java.lang.Instrument.IllegalClassFormatException和一个巨大的StackTrace。 它还会弹出一个错误,上面写着“no tests found with test runner'jun

  • 不重复: 此问题不是链接问题的重复。尽管它询问了使用def和val定义函数之间的区别,但代码示例清楚地表明asker对Scala中方法和函数之间的区别感到困惑。该示例根本没有使用def来定义函数。-7小时前的Aaron Novstrup

  • 但是仅仅从使用的角度来看,就像在指南中一样,以下两者之间的区别在哪里 从这个答案中我可以看出,对于getter重写,该值没有存储。getter覆盖与赋值有什么不同吗?也许是代表或者拉丁裔?

  • 我需要从java调用scala代码,因此需要告诉编译器某个方法抛出某些异常。对于一个异常很容易做到这一点,但是我很难声明一个方法抛出多个异常。 这不起作用: