Scala 如何初始化trait';什么是潜台词?

Scala 如何初始化trait';什么是潜台词?,scala,traits,Scala,Traits,我尝试在trait中使用抽象的val来初始化另一个值。我得到了一个NullPointerException。我将行为归结为一个最小的测试用例: trait MessagePrinter { val message: String println(message) } class HelloPrinter extends MessagePrinter { val message = "Hello World" } val obj = new HelloPrinter() print

我尝试在trait中使用抽象的
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

描述这种行为的来源之一是“拼图4:

以下规则控制初始化和重写行为 VAL的数量:

  • 超类在子类之前完全初始化
  • 成员按声明顺序初始化
  • 当val被重写时,它仍然只能初始化一次
  • 与抽象val一样,重写的val在构造超类期间将具有默认初始值
  • 根据的第5.1节,首先初始化超类。尽管通常无法重新实例化
    VAL
    ,但它们在构造过程中确实以默认初始值开始。您可以使用具有不同语义的
    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
    初始化应按需要工作。

    可能的解决方法:
    lazy val message
    def message
    。不推荐使用的
    delayedInit
    ()也有类似的用途。这是一个JVM兼容性工件:遗憾的是,懒惰的VAL不能是抽象的。