Scala递归val行为



你觉得打印出来的是什么?

val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)

答:

foonull
3

为什么?规格书中有描述/解释这一点的部分吗?

编辑:澄清我的惊讶-我确实意识到fooval 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。默认值为nullfoo2Int,默认值为0。

在这两种情况下,您都引用了未初始化的val,因此使用默认值

在这两种情况下,foo代表p。根据JVM规范,foo2有其默认值。这是null用于引用类型,0用于intInt -正如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

所以,在这里你可以看到你一开始没有看到的警告

相关内容

  • 没有找到相关文章

最新更新