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

发布于 2024-10-17 00:51:28 字数 1827 浏览 2 评论 0原文

我不小心遇到了这样的情况(该示例被简化以隔离问题):

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 Maindef main 行,并关闭 } s),我得到了这样的异常的无限序列(不是真正的无限 - 在某些时候列表停止,我猜是由于异常跟踪的深度或其他原因):

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)
...

我希望至少得到一些东西在运行时提供信息...

好的。我的咆哮结束了。现在我想我应该问一些事情。 :) 那么,您能为案例对象推荐任何一种不错的设计吗?顺便说一句,在我的实际情况中,有几个对象以循环方式指向下一个和上一个实例(最后一个指向第一个实例,反之亦然)。

使用 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
}

这似乎可以在编译版本中工作(但不能作为 Scala 脚本!)。有人能解释一下这里发生的事情吗?

编辑2:这作为脚本工作(同样的事情,只是使用defs):

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

I accidentally ran into a situation like this (the example is simplified to isolate the problem):

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)
  }
}

Anyone would like to guess the output? :-)

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

The output makes kind of sense. Apparently at the time the Second object is created, the First one does not yet exist, therefore null is assigned. The problem is... It's so wrong! It took me a couple of hours to track this one down. Shouldn't the compiler tell something about this?
Interestingly, when I tried to run the thing as a Scala script (the same code, minus object Main and def main lines, and closing }s), I got an infinite sequence (not really infinite - at some point the list stops, I guess due to some limitation on the depth of Exception traces, or something) of exceptions like this:

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)
...

I'd love to get something at least as informative during runtime...

Ok. I finished my rant. Now I guess I should ask something. :)
So, could you recommend any nice design for case objects pointing one to another? By the way, in my real situation there are several objects pointing to the next and previous instances in circular way (the last one points to the first one and vice versa).

Using Scala 2.8.1-final

EDIT:
I found a solution for my main problem:

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

This seems to work in compiled version (but not as a Scala script!). Could anyone shed some light on what's going on here?

EDIT2: This works as a script (the same thing, just using defs):

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

快乐很简单 2024-10-24 00:51:28

通常的方法是这样的(更改了嵌套,以便您可以将其粘贴到 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 中以供将来参考。


编辑:您发现的修复有效,因为对象本身是惰性的,您可以引用它们,但在您使用它们之前它们不会被创建。因此,一个对象可以自由地将自己指向另一个对象,而不需要另一个对象已经初始化。

The usual way is like this (changed nesting so you can paste it into the 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)
  }
}

That is, take a by-name parameter and stick it into a lazy val for future reference.


Edit: The fix you found works because objects are themselves lazy in that you can refer to them but they don't get created until you use them. Thus, one object is free to point itself at the other without requiring that the other one has been initialized already.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文