Scala поведение инициализации

Пожалуйста, просмотрите следующий код.

trait MyTrait { val myVal : String }

class MyClass extends MyTrait { val myVal = "Value" }

class MyClass2(val myVal: String) extends MyTrait 

Почему порядок инициализации отличается в случае MyClass и MyClass2? Конструктор MyClass будет иметь вид

MyClass() {
  MyTrait$class.$init$(this);
  myVal = value
}

Конструктор MyClass2 будет

MyClass2(String myVal) { this.myVal = myVal; MyTrait$class.$init$(this) }

Я думаю, что порядок инициализации должен быть как конструктор MyClass2, тот же для обоих случаев.

Ответы

Ответ 1

В конце раздела 5.1 Scala спецификация определено следующее:

Оценка шаблона. Рассмотрим шаблон sc с mt 1 с mt n {stats}. Если это шаблон признак (п. 5.3.3), то его mixin-оценка состоит из eval- последовательности операторов Статистика. Если это не шаблон, то его оценка состоит из выполните следующие действия.

  • Сначала вычисляется конструктор суперкласса sc (§5.1.1).
  • Затем все базовые классы в линеаризации шаблонов (п. 5.1.2) вверх к указанному суперклассу шаблонов по sc оцениваются по mixin. Оценка миксинов в обратном порядке порядок возникновения в линеаризация.
  • Наконец, оценивается статистика последовательности операторов.

Обратите внимание, однако, что параметры конструктора могут использоваться любыми последующими конструкторами. Поэтому перед этим необходимо выполнить инициализацию. Это делается явным в конце раздела 5.1.1:

Оценка конструктора invocation x.c.,. (argsn) состоит из следующих шаги:

  • Сначала оценивается префикс x.
  • Затем аргументы args1,.,, argsn оцениваются слева направо право.
  • Наконец, построенный класс инициализируется путем оценки шаблон класса, на который ссылается с.

У вас нет проблем, но у вас проблема с {stats} исполняется последним. Причина, по которой {stats} выполняется последним, заключается в том, что она может ссылаться на атрибуты ее классов и свойств предков, тогда как предки, очевидно, не знают о своих потомках. Таким образом, предки должны быть полностью инициализированы до выполнения {stats}.

Конечно, возможно, вам нужна ранняя инициализация. Об этом говорится в разделе 5.1.6: "Ранние определения". Вот как вы его напишете:

class MyClass extends { val myVal = "Value" } with MyTrait