我有这段代码
private static Set<String> myField;
static {
myField = new HashSet<String>();
myField.add("test");
}
它有效。但是当我翻转订单时,我收到非法转发参考错误。
static {
myField = new HashSet<String>();
myField.add("test"); // illegal forward reference
}
private static Set<String> myField;
我有点震惊,我没想到Java有这样的东西。 :)
这里发生了什么?为什么声明的顺序很重要?为什么赋值工作而不是方法调用?
答案 0 :(得分:10)
首先,让我们讨论一下“前向参考”是什么以及为什么它是坏的。前向引用是对尚未初始化的变量的引用,并且它不仅限于静态初始化器。这些都很糟糕,因为如果允许的话,它们会给我们带来意想不到的结果。看看这段代码:
public class ForwardRef {
int a = b; // <--- Illegal forward reference
int b = 10;
}
这个类初始化时应该是什么?初始化类时,将按照从第一个到最后一个遇到的顺序执行初始化。因此,你期望这一行
a = b;
在执行之前执行:
b = 10;
为了避免这种问题,Java设计者完全不允许使用前向引用。
修改强>
此行为由section 8.3.2.3 of Java Language Specifications指定:
成员的声明只有在成员是类或接口C的实例(分别是静态)字段并且满足以下所有条件时才需要出现:
用法发生在C的实例(分别是静态)变量初始值设定项或C的实例(分别是静态)初始值设定项中。
用法不在作业的左侧。
C是封闭用法的最里面的类或接口。
如果不满足上述三个要求中的任何一个,则会发生编译时错误。
答案 1 :(得分:2)
试试这个:
class YourClass {
static {
myField = new HashSet<String>();
YourClass.myField.add("test");
}
private static Set<String> myField;
}
它应该根据JLS编译而没有错误...
(真的没有帮助,或者?)
答案 2 :(得分:1)
在Java中,所有初始值设定项(静态或其他)都按它们在类定义中出现的顺序进行评估。
答案 3 :(得分:1)
请参阅the JLS中的前向引用规则。如果出现以下情况,则无法使用前向引用:
由于所有这些都适用于您的示例,因此前向引用是非法的。
答案 4 :(得分:1)
详细说明DFA的答案:
我认为绊倒你的是JLS 8.2.3.2中第二个要点中的“左手边”规则。在初始化中,myField位于左侧。在您的添加电话中,它位于右侧。这里的代码是隐含的:
boolean result = myField.add('test')
您没有评估结果,但编译器仍然就像它在那里一样。这就是你的初始化在你的add()调用失败时通过的原因。
至于为什么这是如此,我不知道。我知道,这可能是为了方便JVM开发人员。
答案 5 :(得分:0)
我认为方法调用存在问题,因为如果没有add()
的引用类型,编译器无法确定使用哪个myField
方法。
在运行时,使用的方法将由对象类型确定,但编译器只知道引用类型。