Демо scala код:
trait A {
val a = 3
val b = a + 2
}
trait B extends A {
override val a = 10
}
object X extends B
println(X.b)
Он печатает значение: 2, почему это не 5 или 12?
Демо scala код:
trait A {
val a = 3
val b = a + 2
}
trait B extends A {
override val a = 10
}
object X extends B
println(X.b)
Он печатает значение: 2, почему это не 5 или 12?
Чтобы ответить на вопрос, почему:
В Scala, когда вы пишете
class A {
val a = 2
}
Значение инициализируется в конструкторе класса (такое же поведение относится к объектам и объектам). Кроме того, суперклассы инициализируются перед подклассами. Это приводит к следующему поведению для вашего случая использования:
B (зарезервирована память), с двумя переменными a и B, значение которых равно 0. Теперь вызывается конструктор a. Поскольку a перезаписывается в подклассе и из-за динамического связывания Scalas, он не присваивается с 2, а со значением подкласса. Вы хотите, чтобы это было 10, но поскольку это назначение происходит в конструкторе B (который еще не вызывается), присваивается значение по умолчанию 0. Теперь назначается B. Поскольку он не перезаписывается, выбирается значение a+2, где a равно 0. Поскольку конструктор a завершен здесь, может быть вызван конструктор B, который присваивает значение 10 на a.
Следовательно, a равно 10 и B равно 2.
Чтобы ответить, что делать с ошибкой этого поведения:
Не используйте vals, если вы не совсем понимаете проблемы, которые могут возникнуть. Вместо этого используйте defs или lazy vals, там значения не инициализируются в конструкторе класса и поэтому могут быть легко перезаписаны. Если вам абсолютно нужен val в признаке, сделайте его окончательным
Можно пометить переменную как инициализацию, независимую для подкласса, которая может быть выполнена с помощью var a: Type = _. Это говорит компилятору не инициализировать эту переменную в конструкторе определяющего класса (но означает, что значение должно оставаться изменчивым). Затем он может быть легко назначен в подклассе. Это становится важным, когда вызывается в конструкторе суперкласса как метод, который инициализирует var:
class A {
f()
def f() = ()
}
class B extends A {
// don't initialize this var with anything else here or
// the later assignment will be overwritten
var b: Int = _
override def f() =
b = 5
}
new B().b // prints 5