Scala在类/对象初始化中的奇怪行为

时间:2012-11-22 01:49:35

标签: class scala object constructor initialization

  

可能重复:
  Scala and forward references

以下是否有任何理由在Scala中有效:

版本1

object Strange extends App {
  val x = 42
  Console.println(x) // => outputs "42", as expected
}

第2版

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无法解析为变量”时失败。

2 个答案:

答案 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