静态最终字段的非正向引用错误

我正在尝试编译一个javac拒绝的Java类,其中包含非法的前向引用错误,其中违规引用是引用字段之后的词法上。 在显示相同行为时,尽可能地删除以下类:

java.util.concurrent.CallableObject的许多用法仅用作占位符来删除不相关的代码片段。

 public class Test { static final Object foo = method(new java.util.concurrent.Callable() { @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的声明引用foofoo应该在bar的声明范围内。 但是只要删除了foo声明中bar的引用,例如通过从return bar;改变第5行return bar; return null; ,该类被编译器接受。

怎么解释这个? 我对前进的理解是错误之后词汇上的意义还是这个我不知道的特殊情况?

您对前向参考的理解是正确的。 对第9行的foo的引用根本不是前向引用 ,因为它在声明之前不会以文本forms出现(请参阅Java语言规范第8.3.2.3节中的前向引用的定义)。

您观察到的行为是javac 错误的症状。 请参阅此错误报告 。 问题似乎在较新版本的编译器中得到修复,例如OpenJDK 7 。

它仅影响用作最终字段的初始值的前向引用 。 该问题似乎同样影响静态和非静态字段。

请注意, call()bar的引用是合法的前向引用,因为它出现在不同的类中(请参阅Java语言规范的第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; 

使用Test.foo (由Tom Anderson找到)或在非静态情况下使用Test.foo初始化bar

 static final Object bar = Test.foo; 

call()使用foo删除bar并引用该对象:

 static final Object foo = method(new java.util.concurrent.Callable() { @Override public Object call() throws Exception { return foo; } }); 

Java语言规范特别提到了在初始化阶段对对象字段的限制,特别是( C是接口或类):

满足这些条件时,会发生前向引用的编译时错误:

  • 用法发生在C的实例(分别是静态)变量初始化器或C的实例(分别是静态)初始化器中。
  • 用法不在作业的左侧。
  • 用法是通过一个简单的名称。
  • C是封闭用法的最内层类或接口。

文章什么是前向参考规则? 在初始化成员和转发引用时,包含对规则和限制的出色解释。