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.