Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala递归val行为_Scala_Scalac - Fatal编程技术网

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