考虑这个基本的Scala示例代码:
object Test {
def main(args: Array[String]) {
inner()
var x: Int = 5
def inner() {
println("x: " + x)
}
}
}
尝试编译它会产生以下错误消息:
test.scala:3: error: forward reference extends over definition of variable x
inner()
^
one error found
问题:
这个问题并不是关于定义的顺序,而是确切地调用函数的时候。 在定义函数之前调用函数是完全合法的 - 但如果在调用和函数定义之间放置一个变量,它会突然变为非法,并且函数使用此变量。
我想解释这个语言功能!为什么会这样?它是如何工作的?还有其他一些更复杂的例子 - 即它只是某些其他特征的一部分还是某些规则的结果?
我想象编译器目前在做什么:
我基本上回答了我的第三个问题吗?这是这种行为的工作原理吗?它似乎使编译器复杂化(特别是如果我们考虑具有多级函数的情况)。
如果是这种情况,它如何融入语言的正式定义,即语法?在我看来,我写的程序在语法上是正确的。
答案 0 :(得分:5)
来自http://www.scala-lang.org/docu/files/ScalaReference.pdf:
声明或定义引入的名称范围是整个声明 包含结合的序列。但是,对前进有限制 块中的引用:在语句序列中s1 ... sn构成块,如果是简单的话 si中的名称是指由sj定义的实体,其中j> = i,然后是和之间的所有sk 包括si和sj,
•sk不能是变量定义。
•如果sk是一个值定义,它必须是懒惰的
答案 1 :(得分:3)
此错误消息表示不允许在块中进行前向引用。在一个块中,所有变量(或值)必须以线性顺序定义。
请注意,类或对象中允许使用前向引用,但不允许使用块(方法定义)。例如:
这将有效:
object Test { // forward reference is allowed in an object
def inner() {
println("x: " + x)
}
var x: Int = 5
}
这也有效:
class Test { // forward reference is allowed in an class
def inner() {
println("x: " + x)
}
var x: Int = 5
}
但这些都不会起作用:
def main() { // forward reference for vals or vars is not allowed in a block
inner()
def inner() {
println("x: " + x)
}
var x: Int = 5
}
def main() {
inner()
var x: Int = 5
def inner() {
println("x: " + x)
}
}
由于val“x”是字段初始化语句,因此在初始化之前引用它是非法的(“x”在初始化之前包含null)。
为了使其正常工作,您可以将var更改为惰性值:
def main() { // forward reference for lazy vals is allowed in a block
def inner() {
println("x: " + x)
}
lazy val x: Int = 5
}
在惰性val表达式之前调用调用lazy val的方法的一个合法示例:
def main() {
inner()
def inner() {
println("x: " + x)
}
lazy val x: Int = 5
}
如果没有“x”是懒惰的,“x”将被立即初始化,这会破坏Scala编译器执行的重新检查阶段。
对于这种行为背后的一点理论,你可以看一下:https://wiki.scala-lang.org/display/SIW/Overview+of+Compiler+Phases
的“阶段重新检查”部分