Scala - 具有 App 特征的多重继承

Scala - Multiple inheritance with App trait

考虑两个特征,TestTrait1 和 TestTrait,并假设 NewObject 扩展了这两个特征。 这个想法是在 TestTrait 中使用 TestTrait1 中的变量。下面的代码工作得很好。

scala> trait TestTrait1 {
 | val arguments1: Array[String] = Array("1","2")
 | }

defined trait TestTrait1

scala> trait TestTrait {
 | val arguments: Array[String]
 | val len = arguments.length
 | }

defined trait TestTrait

scala> object NewObject extends TestTrait1 with TestTrait {
 |  lazy val arguments = arguments1
 | }

defined object NewObject

scala> NewObject
res30: NewObject.type = NewObject$@7c013560

现在用 App 替换 TestTrait1。由于参数设置为延迟评估,我假设即使在 DelayedInit 的情况下,下面的代码也能工作。

scala> object NewObject extends App with TestTrait {
 | lazy val arguments = args
 | }

但事实并非如此。这背后的原因是什么?

scala> NewObject
java.lang.NullPointerException
at TestTrait$class.$init$(<console>:12)
... 35 elided

如果是这种情况,在另一个类似于 TestTrait 的特征中使用 args 的解决方案是什么?

trait TestTrait1 {
  val arguments1: Array[String] = Array("1","2")
}

trait TestTrait {
  val arguments: Array[String]
  val len = arguments.length
}

如果您看到差异,则 TestTrait 有一个成员 len 会急切地进行初始化。但是 argsApp 中的 def,它恰好有一个默认值 null。如果您将 len 更改为 lazy valdef 它不会因 NPE 而爆炸。

让我们在快速 REPL 会话中尝试一下:

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait TestTrait {
  def arguments: Array[String]
  lazy val len = arguments.length
}

object NewObject extends App with TestTrait {
  override lazy val arguments = super.args // Added `override` and `super` just for clarity.
}

// Exiting paste mode, now interpreting.

defined trait TestTrait
defined object NewObject

scala> NewObject
res0: NewObject.type = NewObject$@5ace1ed4

scala> NewObject.arguments
res1: Array[String] = null

如果您想重现该问题,可以按以下方式致电 len

scala> NewObject.len
java.lang.NullPointerException
  at TestTrait$class.len(<console>:12)
  at NewObject$.len$lzycompute(<console>:15)
  at NewObject$.len(<console>:15)
  ... 33 elided

所以,你的问题的答案是,如果你想调用 NewObject 的实例,你需要使 len 成为 lazy valdef。我建议将 NewObject 设为 classtrait,因为您不希望 unsafe/eagerly 初始化的 len 成员会因 NPE 而爆炸。