scala:创建对象时的循环引用?

Vil*_*tas 8 scala circular-reference

我不小心遇到了这种情况(简化示例以解决问题):

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

有人想猜测输出吗?:-)

e1: First   e1.other: Second
e2: Second   e2.other: null
Run Code Online (Sandbox Code Playgroud)

输出有点意义.显然,在创建第二个对象时,第一个对象尚不存在,因此null被分配.问题是......这是错的!我花了几个小时跟踪这个.编译器不应该说出这个吗?有趣的是,当我尝试将事物作为Scala脚本运行时(相同的代码,减号object Maindef main行,以及关闭}s),我得到了一个无限的序列(不是真的无限 - 在某些时候列表停止,我想由于一些限制关于Exception痕迹的深度,或类似的异常,如下所示:

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)
...
Run Code Online (Sandbox Code Playgroud)

我希望在运行时至少能获得一些信息......

好.我完成了我的咆哮.现在我想我应该问一些事情.:)那么,你能推荐一个很好的设计用于指向彼此的案例对象吗?顺便说一句,在我的实际情况中,有几个对象以循环方式指向下一个和前一个实例(最后一个指向第一个实例,反之亦然).

使用Scala 2.8.1-final

编辑: 我找到了解决我的主要问题的方法:

abstract class Element {
  val other: Element
}
case object First extends Element {
  val other = Second
}
case object Second extends Element {
  val other = First
}
Run Code Online (Sandbox Code Playgroud)

这似乎适用于编译版本(但不是作为Scala脚本!).有人能说清楚这里发生了什么吗?

EDIT2:这作为一个脚本(同样的事情,只使用defs):

abstract class Element { def other: Element }
case object First extends Element { def other = Second }
case object Second extends Element { def other = First }
Run Code Online (Sandbox Code Playgroud)

Rex*_*err 9

通常的方法是这样的(更改嵌套,以便您可以将其粘贴到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)
  }
}
Run Code Online (Sandbox Code Playgroud)

也就是说,取一个by-name参数并将其粘贴到一个懒惰的val中以供将来参考.


编辑:您找到的修复程序有效,因为对象本身很懒,您可以引用它们,但在您使用它们之前不会创建它们.因此,一个对象可以自由地指向另一个对象而不需要已经初始化另一个对象.