Почему, переопределяя черту, почему это странно?

Демо 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?

Ответ 1

Чтобы ответить на вопрос, почему:

В 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