以下是否有任何理由在Scala中有效:
object Strange extends App {
val x = 42
Console.println(x) // => outputs "42", as expected
}
object Strange extends App {
Console.println(x) // => "0" ?!
val x = 42
}
为什么编译,为什么在没有任何警告的情况下表现得如此奇怪?
class
也是同样的问题:
class StrangeClass {
Console.println(x) // => still "0"
val x = 42
}
object TestApp extends App {
new StrangeClass()
}
普通方法的正文没有这样的问题:
def nonStrangeMethod {
Console.println(y) // => fails with "not found: value y", as expected
y = 42
}
如果我们将“final”添加到val声明中,行为会发生显着变化:
class StrangeClass {
Console.println(x) // => "42", but at least that's expected
final val x = 42
}
对于记录,以下Java静态(Scala的object
)对应部分:
public class Strange {
static {
System.out.println(x);
}
static int x = 42;
public static void main(String[] args) {}
}
无法使用plain& amp;在第3行和Java非静态(Scala的class
)对应物上可以理解的错误“无法在定义之前引用字段”:
public class Strange {
Strange() {
System.out.println(x);
int x = 42;
}
public static void main(String[] args) {
new Strange();
}
}
显然在第3行“x无法解析为变量”时失败。
答案 0 :(得分:3)
这是因为使用延迟初始化的App
特征。来自 Scala编程关于App
特征:
花括号之间的代码被收集到主要代码中 单例对象的构造函数,并在类为时执行 初始化
2.9.0版也注意到了这一点:
继承App特征的对象改为使用Scala 2.9 延迟初始化功能执行整个身体作为一部分 继承主要方法。
所以Console.println(x)
在Strange
运行之前不执行,你可以将其作为输出:
scala> s.main(Array[String]())
0
如果您在Console.println(x)
之后添加另一个val x = 42
,则会打印出来:
scala> s.main(Array[String]())
0
42
编译器知道当前作用域中存在x
,但它会延迟对它的评估,直到它被执行,然后打印出Int
的默认值0
。 / p>
答案 1 :(得分:2)
答案基本上就是编译器如何构造事物。由于Scala中没有静态变量 - 初始化基本上发生在构造函数中,因此第二个示例的Java等价物类似于:
public class Strange {
int x = 0;
public Strange() {
System.out.println(x);
x = 42;
}
}
哪个编译好。我假设编译器将uninitialized int的值设置为0以避免NPE。如果颠倒了构造函数中语句的顺序,您将获得第一个示例中描述的行为。
此问题还有一些细节:Scala and forward references