在scala Intellij工作表中初始化两个相互依赖的值



下面的结果都在Intellij IDEA Scala工作表中,但标准Scala REPL似乎一致,因此这可能是由于工作表的包装。

scala puzzlers网站上有一个谜题。

object XY {
  object X {
    val value: Int = Y.value + 1
  }
  object Y {
    val value: Int = X.value + 1
  }
}
println(if (math.random > 0.5) XY.X.value else XY.Y.value)

提到的正确答案是2(对于scala 2.10.0?)。然而,在Intellij工作表(scala 2.11.7)中,它会发出stackoverflowError。

另外,正如规范

中提到的

对象定义定义了一个符合模板tt的对象(或:module)。它大致相当于以下惰性值

的定义

确实下面也会给出stackoverflowError。

lazy val x: Int = y + 1
lazy val y: Int = x + 1

这里有一些其他的观察。

案例1

object XXX {
  val x: Int = y + 1
  lazy val y: Int = x + 1
}
s"XXX x=${XXX.x} y=${XXX.y}"
// XXX x=2 y=1

案例2

val x: Int = y + 1
s"plain x=$x"
lazy val y: Int = x + 1
s"plain y=$y"
// stackoverflowError

情况3

object XXX {
  val x: Int = y + 1
  lazy val y: Int = x + 1
}
s"XXX x=${XXX.x} y=${XXX.y}"
val x: Int = y + 1
s"plain x=$x"
lazy val y: Int = x + 1
s"plain y=$y"
//  XXX x=2 y=1
// NO output any longer for subsequent scripts

是已知的Intellij scala插件错误吗?

在Scala 2.11.8上我没有得到错误,我猜这与你的设置中的其他东西有关。你给出的链接很好地解释了为什么这样做——它基本上归结为JVM初始化魔法,具体来说:

VM注意到对象X已经初始化运行并返回X.value的当前值,该值为零(Int字段的默认值),因此在运行时不会发生堆栈溢出。

然而,没有理由让这种魔力也适用于

lazy val x: Int = y + 1
lazy val y: Int = x + 1

应该是堆栈溢出

<标题>案例1

这里没有什么奇怪的-这只是初始化之前字段的默认值为0的通常情况。在x的初始化期间(首先发生),y的初始化被触发,它使用x的当前值(默认为0,因为x尚未初始化)并计算y = 0 + 1 = 1。然后,返回初始化x,计算x = 1 + 1 = 2

<标题>案例2 h1> 有点难看,因为这段代码需要在某些东西的内部进行编译(对象、类、函数、方法等等)。在类或对象中,上述推理适用(这就是Scala REPL中发生的事情,因为它基本上只是在对象内部运行裸代码)。 另一方面,如果你把它放在一个函数或方法中,它根本不应该编译,因为你有一个前向引用。它应该像这样失败:
error: forward reference extends over definition of value x
   val x: Int = y + 1
                ^

我想说Intellij IDEA Scala工作表没有正确处理这个问题。

<标题>情况3 关于这件事我什么也不能说。在前面的案例中,我已经介绍了预期的Scala输出——这完全取决于代码是如何包装的(在对象/类或方法/函数中)。再一次,Intellij IDEA Scala工作表似乎不工作。

最新更新