Как инициализировать значения черт в 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 во время инициализации?

2 ответа

Решение

Согласно разделу 5.1 спецификации Scala, суперклассы инициализируются первыми. Даже если 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 инициализация должна работать как нужно.

Вы должны использовать def в обоих случаях.

Одним из источников, описывающих это поведение, является " Scala Puzzlers" Puzzler 4:

Следующие правила управляют инициализацией и переопределением поведения vals:

  1. Суперклассы полностью инициализируются перед подклассами.
  2. Члены инициализируются в порядке их объявления.
  3. Когда val переопределяется, он все еще может быть инициализирован только один раз.
  4. Как и абстрактный val, переопределенный val будет иметь начальное значение по умолчанию во время создания суперклассов.
Другие вопросы по тегам