scala: круговая ссылка при создании объекта? - PullRequest
8 голосов
/ 12 февраля 2011

Я случайно столкнулся с такой ситуацией (пример упрощен, чтобы выделить проблему):

abstract class Element(val other: Element)

case object First extends Element(Second)
case object Second extends Element(First)

object Main {
  def main(arguments: Array[String]) {
    val e1 = First
    val e2 = Second
    println("e1: "+e1+"   e1.other: "+e1.other)
    println("e2: "+e2+"   e2.other: "+e2.other)
  }
}

Кто-нибудь хотел бы угадать вывод? : -)

e1: First   e1.other: Second
e2: Second   e2.other: null

Вывод имеет смысл. По-видимому, на момент создания Второго объекта Первый еще не существует, поэтому присваивается null. Проблема в том ... Это так неправильно! Мне понадобилось несколько часов, чтобы отследить это. Разве компилятор не должен что-то говорить об этом? Интересно, что когда я пытался запустить его как скрипт Scala (тот же код, минус object Main и def main строк и закрытие } с), я получил бесконечную последовательность (не совсем бесконечную - в какой-то момент Список останавливается, я полагаю, из-за некоторого ограничения глубины следов исключений или чего-то такого) исключений, подобных этому:

vilius@blackone:~$ scala 1.scala
...
at Main$$anon$1.Main$$anon$$Second(1.scala:4)
at Main$$anon$1$First$.<init>(1.scala:3)
at Main$$anon$1.Main$$anon$$First(1.scala:3)
at Main$$anon$1$Second$.<init>(1.scala:4)
at Main$$anon$1.Main$$anon$$Second(1.scala:4)
at Main$$anon$1$First$.<init>(1.scala:3)
...

Я бы хотел получить что-то хотя бы информативное во время выполнения ...

Ok. Я закончил свою напыщенную речь. Теперь, наверное, мне следует кое-что спросить. :) Итак, не могли бы вы порекомендовать какой-нибудь красивый дизайн для объектов case, указывающих один на другой? Кстати, в моей реальной ситуации есть несколько объектов, указывающих на следующий и предыдущий экземпляры по кругу (последний указывает на первый и наоборот).

Использование Scala 2.8.1-final

EDIT: Я нашел решение для моей основной проблемы:

abstract class Element {
  val other: Element
}
case object First extends Element {
  val other = Second
}
case object Second extends Element {
  val other = First
}

Это похоже на работу в скомпилированной версии (но не как скрипт Scala!). Может ли кто-нибудь пролить свет на то, что здесь происходит?

EDIT2: Это работает как скрипт (то же самое, только с использованием def s):

abstract class Element { def other: Element }
case object First extends Element { def other = Second }
case object Second extends Element { def other = First }

1 Ответ

9 голосов
/ 12 февраля 2011

Обычный способ подобен этому (измененное вложение, чтобы вы могли вставить его в REPL):

object Main{
  abstract class Element(other0: => Element) {
    lazy val other = other0
  }

  case object First extends Element(Second)
  case object Second extends Element(First)

  def main(arguments: Array[String]) {
    val e1 = First
    val e2 = Second
    println("e1: "+e1+"   e1.other: "+e1.other)
    println("e2: "+e2+"   e2.other: "+e2.other)
  }
}

То есть, взять параметр по имени и вставить его в ленивый val для дальнейшего использования..


Редактировать: Исправление, которое вы нашли, работает, потому что сами объекты ленивы в том, что вы можете ссылаться на них, но они не создаются, пока вы их не используете.Таким образом, один объект может свободно указывать на другой, не требуя, чтобы другой был уже инициализирован.

...