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
会急切地进行初始化。但是 args
是 App
中的 def
,它恰好有一个默认值 null
。如果您将 len
更改为 lazy val
或 def
它不会因 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 val
或 def
。我建议将 NewObject 设为 class
或 trait
,因为您不希望 unsafe/eagerly 初始化的 len
成员会因 NPE 而爆炸。
考虑两个特征,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
会急切地进行初始化。但是 args
是 App
中的 def
,它恰好有一个默认值 null
。如果您将 len
更改为 lazy val
或 def
它不会因 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 val
或 def
。我建议将 NewObject 设为 class
或 trait
,因为您不希望 unsafe/eagerly 初始化的 len
成员会因 NPE 而爆炸。