你觉得打印出来的是什么?
val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)
答:
foonull
3
为什么?规格书中有描述/解释这一点的部分吗?
编辑:澄清我的惊讶-我确实意识到foo
在val foo: String = "foo" + foo
处未定义,这就是为什么它具有默认值null
(整数为零)。但这看起来不太"干净",我在这里看到的观点与我的观点一致。我希望编译器能阻止我做这样的事情。在某些特定情况下,它确实是有意义的,例如定义本质上懒惰的Stream
时,但是对于字符串和整数,我希望由于重新赋值给val而阻止我,或者告诉我我试图使用未定义的值,就像我写val foo = whatever
一样(给定whatever
从未定义)。
为了使事情进一步复杂化,@dk14指出这种行为只出现在以字段表示的值上,而不会发生在块中,例如
val bar: String = {
val foo: String = "foo" + foo // error: forward reference extends...
"bar"
}
可以,参见SLS章节4.2。
foo
是一个引用类型String
。默认值为null
。foo2
为Int
,默认值为0。
在这两种情况下,您都引用了未初始化的val,因此使用默认值
在这两种情况下,foo
代表p。根据JVM规范,foo2
有其默认值。这是null
用于引用类型,0
用于int
或Int
-正如Scala拼写的那样。
Scala的Int
是由下面的原始类型(int
)支持的,它的默认值(初始化之前)是0。
另一方面,String
是一个未初始化的Object
,它实际上是null
。
不幸的是,scala并不像Haskell那样安全,因为它与Java的OOP模型("面向对象的功能")相妥协。Scala有一个安全的子集叫做Scalazzi,一些sbt/Scala插件可以给你更多的警告/错误:
https://github.com/puffnfresh/wartremover(不检查您发现的情况)
回到你的例子,这只发生在表示为字段的值上,当你在函数/方法/块中时不会发生:
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
所以,在这里你可以看到你一开始没有看到的警告