如何在 subtrait 中初始化 trait 的 vals?

How to initialize trait's vals in subtrait?

我尝试在特征中使用抽象 val 来初始化另一个值。我得到了 NullPointerException。我将行为归结为最小的测试用例:

trait MessagePrinter {
  val message: String
  println(message)
}

class HelloPrinter extends MessagePrinter {
  val message = "Hello World"
}

val obj = new HelloPrinter()
println(obj.message)

这个小程序产生以下结果:

null
Hello World

我的印象是 val 可能永远不会改变。这是预期的行为还是编译器错误?如何解决此问题并在初始化期间打印 Hello World

你应该在这两种情况下使用 def

描述此行为的来源之一是“Scala Puzzlers”Puzzler 4:

The following rules control the initialization and overriding behavior of vals:

  1. Superclasses are fully initialized before subclasses.
  2. Members are initialized in the order they are declared.
  3. When a val is overridden, it can still only be initialized once.
  4. Like an abstract val, an overridden val will have a default initial value during the construction of superclasses.

Scala specification的5.1节中,super类首先被初始化。尽管 vals 通常无法重新实例化,但它们在构造期间确实以默认初始值开始。您可以使用具有不同语义的 def

trait MessagePrinter {
   def message: String
   println(message)
}

class HelloPrinter extends MessagePrinter {
   def message = "Hello World"
}

或者您可以考虑像这样切换:

class HelloPrinter extends { val message = "Hello World" } with MessagePrinter 

在这种情况下,超级 类 会按顺序进行评估,因此 MessagePrinter 初始化应该按预期工作。