Class Scala在类/对象初始化中有一种奇怪的行为

Class Scala在类/对象初始化中有一种奇怪的行为,class,scala,object,constructor,initialization,Class,Scala,Object,Constructor,Initialization,可能重复: 以下内容在Scala中起作用的原因是什么 版本1 版本2 为什么它会编译,为什么在没有任何警告或任何东西的情况下表现得如此怪异 类也存在同样的问题: class StrangeClass { Console.println(x) // => still "0" val x = 42 } object TestApp extends App { new StrangeClass() } 常规方法的主体不存在此类问题: def nonStrangeMethod {

可能重复:

以下内容在Scala中起作用的原因是什么

版本1 版本2 为什么它会编译,为什么在没有任何警告或任何东西的情况下表现得如此怪异

也存在同样的问题:

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
}
如果我们在val声明中添加“final”,行为会发生显著变化:

class StrangeClass {
  Console.println(x) // => "42", but at least that's expected
  final val x = 42
}
对于记录,以下Java静态(Scala的
对象
)对应项:

public class Strange {
    static {
        System.out.println(x);
    }
    static int x = 42;

    public static void main(String[] args) {}
}
public class Strange {
    Strange() {
        System.out.println(x);
        int x = 42;
    }

    public static void main(String[] args) {
        new Strange();
    }
}
编译失败,在第#3行和Java非静态(Scala的
)对应项上出现简单易懂的错误“无法在定义字段之前引用字段”:

public class Strange {
    static {
        System.out.println(x);
    }
    static int x = 42;

    public static void main(String[] args) {}
}
public class Strange {
    Strange() {
        System.out.println(x);
        int x = 42;
    }

    public static void main(String[] args) {
        new Strange();
    }
}

显然,第3行的“x不能解析为变量”失败。

答案基本上是编译器如何构造东西。由于Scala中没有静态变量-初始化基本上发生在构造函数中,因此第二个示例的Java等价物类似于:

public class Strange {
    int x = 0;

    public Strange() {
        System.out.println(x);
        x = 42;
    }
}
那就好了。我假设编译器将未初始化int的值设置为0以避免NPE。如果颠倒了构造函数中语句的顺序,将得到第一个示例中描述的行为


这个问题还有一些细节:

这是因为
App
特性使用延迟初始化。根据Scala中有关
App
特性的编程:

大括号之间的代码被收集到主 单例对象的构造函数,并在类 初始化

2.9.0版本也说明了这一点:

继承应用特性的对象使用Scala 2.9 延迟初始化功能,将整个身体作为 继承了main方法

因此,
Console.println(x)
不会执行,直到运行了
Strange
并将其作为输出:

scala> s.main(Array[String]())
0
如果在
val x=42
之后添加另一个
Console.println(x)
,则它会打印出:

scala> s.main(Array[String]())
0
42

编译器知道当前作用域中存在
x
,但它会延迟对它的求值,直到它被执行,然后它会打印出
Int
的默认值,即
0

App
trait和
object
都不是这里的问题。我可以用
class
的构造函数复制完全相同的行为,而没有任何特征。因此,您指定的行为似乎引用了任何类似构造函数的代码。这并不能真正回答为什么会这样。这样的行为打破了很多期望(在缺少变量名的情况下会出现编译错误,
val
s在运行时更改其值,这与常量值契约相反,代码在构造函数和普通方法中的行为相同),我看不到它给世界带来任何好处。也许有什么我不知道为什么有必要用这种方式处理它?因为对象不使用静态值,它们可以在子类中重写,否则就可以享受OO设计的所有好处。因此,需要在内存中分配对象,然后由适当的构造函数初始化其值,而不是静态值,静态值不能被重写,可以直接设置。我可能有点走火入魔了,因为我从未真正深入研究过编译器设计,但我相信这就是原因所在。但是我不能说太多的期望。