我正在尝试编译一个javac
拒绝带有非法转发引用错误的Java类,其中违规引用在引用字段之后是词法 。在显示相同行为时,尽可能地删除以下类:
java.util.concurrent.Callable
以及Object
的许多用法仅用作占位符来删除不相关的代码段。
public class Test {
static final Object foo = method(new java.util.concurrent.Callable<Object>() {
@Override
public Object call() throws Exception {
return bar;
}
});
static final Object bar = foo;
static Object method(Object binder) {
return null;
}
}
使用javac Test.java
编译时,javac会输出以下错误消息:
Test.java:9: illegal forward reference
static final Object bar = foo;
^
因此编译器抱怨bar
的声明引用foo
,而foo
应该在bar
声明的范围内。但是,只要删除了bar
声明中foo
的引用,例如通过将第5行从return bar;
更改为return null;
,编译器可以接受该类。
如何解释?在错误之后,我对转发的理解是意味着什么?或者这是一些我不知道的特殊情况?
答案 0 :(得分:16)
您对转发参考的理解是正确的。对第9行的foo
的引用根本不是前向引用,因为它在声明之前不会以文本形式出现(请参阅前向引用的构成定义< / em>在The Java Language Specification)的第8.3.2.3节中。
您观察到的行为是javac bug 的症状。见this bug report。问题似乎在较新版本的编译器中得到修复,例如OpenJDK 7
它仅影响用作最终字段的初始值设定项的转发引用。该问题似乎同样影响静态和非静态字段。
请注意bar
中对call()
的引用是合法的转发引用,因为它出现在另一个类中(参见{{3}的第8.3.2.3节中的示例}})。
另请注意,以下每项更改都会使错误消失:
使bar
成为非决赛:
static Object bar = foo;
在静态或实例初始化程序块中初始化bar
:
static final Object bar;
static {
bar = foo;
}
将foo
的初始化移动到初始化块也有帮助。
将bar
从非最终临时引用初始化为foo
:
static Object tmp = foo;
static final Object bar = tmp;
使用bar
(由Tom Anderson找到)或Test.foo
在非静态情况下初始化this.foo
:
static final Object bar = Test.foo;
删除bar
并使用foo
内的call()
引用该对象:
static final Object foo = method(new java.util.concurrent.Callable<Object>() {
@Override
public Object call() throws Exception {
return foo;
}
});
答案 1 :(得分:1)
Java Language Specifications特别提到了初始化阶段对象字段的限制,特别是( C 是一个接口或类):
满足这些条件时,会发生前向引用的编译时错误:
- 用法发生在 C 的实例(分别是静态)变量初始值设定项或 C 的实例(分别是静态)初始值设定项中。
- 用法不在作业的左侧。
- 使用方法是一个简单的名称。
- C是封闭用法的最里面的类或接口。
文章What are the forward reference rules?包含对初始化成员和转发引用时规则和限制的出色解释。