Как инициализировать значения черт в 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:
- Суперклассы полностью инициализируются перед подклассами.
- Члены инициализируются в порядке их объявления.
- Когда val переопределяется, он все еще может быть инициализирован только один раз.
- Как и абстрактный val, переопределенный val будет иметь начальное значение по умолчанию во время создания суперклассов.