Scala и прямые ссылки

Возможный дубликат:
Scala: прямые ссылки - почему этот код компилируется?

object Omg {

  class A

  class B(val a: A)

  private val b = new B(a)

  private val a = new A

  def main(args: Array[String]) {
    println(b.a)
  }

}

следующий код печатает "null". В java. аналогичная конструкция не компилируется из-за недействительной прямой ссылки. Вопрос в том, почему он хорошо компилируется в Scala? Это по дизайну, описанному в SLS или просто ошибка в 2.9.1?

Ответ 1

Это не ошибка, а классическая ошибка при изучении Scala. Когда объект Omg инициализируется, все значения сначала устанавливаются в значение по умолчанию (null в этом случае), а затем выполняется конструктор (т.е. Тело объекта).

Чтобы заставить его работать, просто добавьте ключевое слово lazy перед объявлением, которое вы перенаправляете вперед (значение a в этом случае):

object Omg {

  class A

  class B(val a: A)

  private val b = new B(a)

  private lazy val a = new A

  def main(args: Array[String]) {
    println(b.a)
  }
}

Значение a будет инициализировано по требованию.

Эта конструкция выполняется быстро (значения инициализируются только один раз для всех приложений) и потокобезопасными.

Ответ 2

Как я понимаю, это связано с тем, как создаются классы Scala. В Java класс, определенный выше, будет инициализировать переменные inline, и поскольку a еще не определен, он не может быть скомпилирован. Однако в Scala он больше эквивалентен этому в Java (который также должен генерировать нуль в том же сценарии):

class Omg {
  private B b = null;
  private A a = null;

  Omg(){ 
    b = new B(a);
    a = new A();
  }
}

В качестве альтернативы вы можете сделать объявление b ленивым, что отсрочит установку значения до его вызова (в какое время будет установлено значение a).

Ответ 3

Если это вызывает беспокойство, скомпилируйте с -Xcheckinit во время разработки и итерации до тех пор, пока исключения не исчезнут.

Spec 5.1 для операторов тела шаблона, выполненных по порядку; начало 4.0 для прямых ссылок в блоках.

Forward References - почему этот код компилируется?

Ответ 4

Как @paradigmatic, это не ошибка. Это порядок инициализации, который следует за порядком декларации. В этом случае a имеет значение null, когда b объявляется /init -ed.

Изменение строки private val b = new B(a) на private lazy val b = new B(a) будет исправлять проблему, так как использование ленивого будет задерживать init. b для его первого использования.

Очень вероятно, что это поведение описано в SLS.