Scala 递归 val 行为
Scala recursive val behaviour
你认为打印出来的是什么?
val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)
答案:
foonull
3
为什么?规范中是否有部分 describes/explains 这个?
编辑:为了澄清我的惊讶 - 我确实意识到 foo
在 val foo: String = "foo" + foo
处未定义,这就是为什么它具有默认值 null
(整数为零)。但这似乎不太"clean",我看到这里的意见很赞同我。我希望编译器能阻止我做那样的事情。它在某些特定情况下确实有意义,例如在定义 Stream
s 时,它们本质上是惰性的,但对于字符串和整数,我希望要么因为重新分配给 val 而停止我,要么告诉我我正在尝试使用未定义的值,就像我写 val foo = whatever
一样(假定 whatever
从未定义)。
为了使事情更加复杂,@dk14 指出这种行为只存在于以字段表示的值中,不会发生在块中,例如
val bar: String = {
val foo: String = "foo" + foo // error: forward reference extends...
"bar"
}
Scala 的 Int
由底层的原始类型 (int
) 支持,其默认值(初始化前)为 0。
另一方面,String
是一个 Object
,未初始化时确实是 null
。
是的,请参阅 SLS Section 4.2。
foo
是一个 String
,它是一个引用类型。它的默认值为 null
。 foo2
是一个 Int
,默认值为 0。
在这两种情况下,您指的是一个尚未初始化的 val,因此使用默认值。
在这两种情况下 foo
resp。 foo2
根据 JVM 规范具有默认值。对于引用类型是 null
,对于 int
或 Int
是 0
- 正如 Scala 拼写的那样。
不幸的是,由于与 Java 的 OOP 模型 ("Object-oriented meets Functional") 的妥协,scala 不如我们所说的 Haskell 安全。 Scala 有一个安全的子集,叫做 Scalazzi,sbt/scalac-plugins 中的一些可以给你更多 warnings/errors:
https://github.com/puffnfresh/wartremover (doesn't check 对于你找到的情况)
回到你的案例,这只发生在以字段表示的值上,当你在 function/method/block:
中时不会发生
scala> val foo2: Int = 3 + foo2
foo2: Int = 3
scala> {val foo2: Int = 3 + foo2 }
<console>:14: error: forward reference extends over definition of value foo2
{val foo2: Int = 3 + foo2 }
^
scala> def method = {val a: Int = 3 + a}
<console>:12: error: forward reference extends over definition of value a
def method = {val a: Int = 3 + a}
这种情况的原因是与 Java 集成,因为 val
编译到 JVM 中的 final
字段,因此 Scala 保存了 JVM 类 的所有初始化细节(我相信它是 JSR-133 的一部分:Java 内存模型)。这是解释此行为的更复杂的示例:
scala> object Z{ val a = b; val b = 5}
<console>:12: warning: Reference to uninitialized value b
object Z{ val a = b; val b = 5}
^
defined object Z
scala> Z.a
res12: Int = 0
所以,在这里你可以看到你一开始没有看到的警告。
你认为打印出来的是什么?
val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)
答案:
foonull
3
为什么?规范中是否有部分 describes/explains 这个?
编辑:为了澄清我的惊讶 - 我确实意识到 foo
在 val foo: String = "foo" + foo
处未定义,这就是为什么它具有默认值 null
(整数为零)。但这似乎不太"clean",我看到这里的意见很赞同我。我希望编译器能阻止我做那样的事情。它在某些特定情况下确实有意义,例如在定义 Stream
s 时,它们本质上是惰性的,但对于字符串和整数,我希望要么因为重新分配给 val 而停止我,要么告诉我我正在尝试使用未定义的值,就像我写 val foo = whatever
一样(假定 whatever
从未定义)。
为了使事情更加复杂,@dk14 指出这种行为只存在于以字段表示的值中,不会发生在块中,例如
val bar: String = {
val foo: String = "foo" + foo // error: forward reference extends...
"bar"
}
Scala 的 Int
由底层的原始类型 (int
) 支持,其默认值(初始化前)为 0。
另一方面,String
是一个 Object
,未初始化时确实是 null
。
是的,请参阅 SLS Section 4.2。
foo
是一个 String
,它是一个引用类型。它的默认值为 null
。 foo2
是一个 Int
,默认值为 0。
在这两种情况下,您指的是一个尚未初始化的 val,因此使用默认值。
在这两种情况下 foo
resp。 foo2
根据 JVM 规范具有默认值。对于引用类型是 null
,对于 int
或 Int
是 0
- 正如 Scala 拼写的那样。
不幸的是,由于与 Java 的 OOP 模型 ("Object-oriented meets Functional") 的妥协,scala 不如我们所说的 Haskell 安全。 Scala 有一个安全的子集,叫做 Scalazzi,sbt/scalac-plugins 中的一些可以给你更多 warnings/errors:
https://github.com/puffnfresh/wartremover (doesn't check 对于你找到的情况)
回到你的案例,这只发生在以字段表示的值上,当你在 function/method/block:
中时不会发生scala> val foo2: Int = 3 + foo2
foo2: Int = 3
scala> {val foo2: Int = 3 + foo2 }
<console>:14: error: forward reference extends over definition of value foo2
{val foo2: Int = 3 + foo2 }
^
scala> def method = {val a: Int = 3 + a}
<console>:12: error: forward reference extends over definition of value a
def method = {val a: Int = 3 + a}
这种情况的原因是与 Java 集成,因为 val
编译到 JVM 中的 final
字段,因此 Scala 保存了 JVM 类 的所有初始化细节(我相信它是 JSR-133 的一部分:Java 内存模型)。这是解释此行为的更复杂的示例:
scala> object Z{ val a = b; val b = 5}
<console>:12: warning: Reference to uninitialized value b
object Z{ val a = b; val b = 5}
^
defined object Z
scala> Z.a
res12: Int = 0
所以,在这里你可以看到你一开始没有看到的警告。