我希望这会引发错误,因为我在分配它之前尝试使用
x
。这是 Scala 3.3.1 REPL:
scala> val x: Int =
| val y: Int = x + 10
| y
|
val x: Int = 10
为什么结果是10?当 Scala 计算
x + 10
时,它假设 x=0
?
发生这种情况是因为允许您编译和执行的相同 REPL 技巧
> val x = 1
> val x = 2
在交互式 REPL 模式中:
val
不会编译为函数体内的局部变量,而是编译为包装对象的成员,如此处所述。
在您的特定情况下,当您输入示例时
scala> val x: Int =
| val y: Int = x + 10
| y
|
进入 REPL,它被包装成一个合成的“执行模板”对象,其行为大致如下脚本:
object replInputLine$1 {
val x: Int =
val y = x + 10
y
println(x)
}
@main def entry(): Unit =
replInputLine$1
这个脚本中发生了什么?好了,
replInputLine$1
对象已初始化。与在对象初始化期间一样,成员 x: Int
在分配对象期间被分配并初始化为 0
,然后执行实际的对象初始化程序,将 x
更新为 10
。
由于
x
是对象成员,而不是方法体中的局部变量,因此可以毫无问题地访问它。
如果您尝试在方法体内使用相同的代码片段,则会出现编译错误,如以下示例所示:
@main def entry(): Unit =
val x: Int =
val y: Int = x + 10
y
println(x)
这将使编译失败并显示消息
引用错误:[...] x 是扩展 x 定义的前向引用
所以,这只是 REPL 将
val
视为合成对象的成员而不是将它们视为局部变量这一事实所产生的产物。
用 REPL 玩“语言律师”游戏毫无意义且无聊:如果您想了解编译器“真正”在做什么,请不要在 REPL 中测试它,编写一个单独的程序,然后编译它。