Scala递归val行为
你觉得打印出来的是什么Scala递归val行为,scala,scalac,Scala,Scalac,你觉得打印出来的是什么 val foo: String = "foo" + foo println(foo) val foo2: Int = 3 + foo2 println(foo2) 答复: foonull 3 为什么??规范中是否有描述/解释这一点的部分 编辑:为了澄清我的惊讶-我确实意识到foo在val foo:String=“foo”+foo中没有定义,这就是为什么它有一个默认值null(整数为零)。但这似乎不是很“干净”,我在这里看到的观点与我的一致。我希望编译器能阻止我做那样
val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)
答复:
foonull
3
为什么??规范中是否有描述/解释这一点的部分
编辑:为了澄清我的惊讶-我确实意识到foo
在val foo:String=“foo”+foo
中没有定义,这就是为什么它有一个默认值null
(整数为零)。但这似乎不是很“干净”,我在这里看到的观点与我的一致。我希望编译器能阻止我做那样的事情。在某些特定情况下,它确实是有意义的,例如定义本质上是惰性的流时,但对于字符串和整数,我希望由于重新分配给val而停止我,或者告诉我我试图使用未定义的值,就像我编写val foo=where
(考虑到从未定义过任何内容)
更复杂的是,@dk14指出,这种行为只存在于以字段表示的值中,而不会发生在块中,例如
val bar: String = {
val foo: String = "foo" + foo // error: forward reference extends...
"bar"
}
Scala的Int
由下面的基元类型(Int
)支持,其默认值(初始化前)为0
另一方面,字符串
是一个对象
,它在未初始化时实际上是空的。是,请参阅
foo
是一个String
的引用类型。它的默认值为null
foo2
是一个Int
的默认值为0
在这两种情况下,您都引用了一个尚未初始化的val,因此使用了默认值。在这两种情况下,foo
和foo2
都有其根据JVM规范的默认值。对于引用类型,null
,对于int
或int
,使用Scala拼写它。不幸的是,scala没有Haskell那么安全,因为它与Java的OOP模型(“面向对象满足功能”)有冲突。有一个称为scala的安全子集,一些sbt/scalac插件可以给您更多警告/错误:
(对于您发现的案例)
回到您的案例中,这只会发生在表示为字段的值上,而在函数/方法/块中不会发生:
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}
因此,在这里,您可以看到一开始没有看到的警告。好的,因此这与您试图在某个抽象类中使用一个字段相同,该字段的值尚未在子类中定义(由于线性化的顺序)。但是val
的这个定义不应该被编译器捕获吗?它是val
的一个明确的重新声明。它不是val的重新声明。val只声明了一次,但它在初始化之前就被使用了。没错,编译器可以警告我们这一点。它应该和说val foo一样=which
(假设which
没有在任何地方定义)。但我想发生的是“好的,这里我们初始化了一个val,让我们创建它并为它分配一个空值,直到我们完成初始化”@slouc Heard ya,不幸的是,scala没有Haskell那么安全,因为它与Java有冲突。有一个叫做Scalazzi的安全子集,一些sbt/scalac插件可以给你更多警告/错误:你会期望什么行为?编译器告诉我“重新分配到val”或者告诉我它不知道什么是foo
(因为编译器的词法分析还没有存储这个标记,因为它还没有完全定义)。绝对没有给它临时值null
,这听起来更像JavaScript而不是Scala(当然是未定义而不是null)@slouc还注意到用def
s替换val
s时的行为差异。当然,这是完全合乎逻辑的。但是val
s的这种行为似乎有点“不正常”。我希望相信val
s是真正不可变的,这意味着foo
要么有一些永远不变的值,要么根本不存在。这里没有空值,那里没有xy值。它实际上是JMM(Java内存模型)这就决定了this@slouc不客气。我补充了关于为什么会发生这种行为的说明。很好,谢谢。在你来之前,我已经接受了一个答案,但我承认这个答案更具信息性。
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