静态最终字段的非法前向引用错误
我正在尝试编译一个 Java 类,javac
会因非法前向引用错误而拒绝,其中有问题的引用在词法上在引用的字段之后。下面的类在显示相同行为的同时被尽可能地精简:
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
声明的范围内。但是,一旦 foo
声明中对 bar
的引用被删除,例如将第 5 行从 return bar;
更改为 return null;
,该类被编译器接受。
这该如何解释呢?我对向前的理解是词法上错误,还是这是我不知道的特殊情况?
I'm trying to compile a Java class which javac
rejects with an illegal forward reference error, where the offending reference is lexically after the referenced field. The following class is stripped down as much as possible while showing the same behavior:
java.util.concurrent.Callable
and the many uses of Object
are just used as placeholders to remove irrelevant pieces of code.
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;
}
}
When compiled using javac Test.java
, javac prints the following error message:
Test.java:9: illegal forward reference
static final Object bar = foo;
^
So the compiler complains about bar
's declaration referencing foo
while foo
should be in the scope of bar
's declaration. But as soon as the reference of bar
in foo
's declaration is removed, e.g. by changing line 5 from return bar;
to return null;
, the class is accepted by the compiler.
How can this be explained? Is my understanding of forward as meaning lexically after wrong or is this some special case I'm not aware of?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您对前向引用的理解是正确的。第 9 行对
foo
的引用根本不是前向引用,因为它没有以文本形式出现在其声明之前(请参阅构成 Java 的第 8.3.2.3 节中的前向参考语言规范)。您观察到的行为是 javac bug 的症状。请参阅此错误报告。该问题似乎已在较新版本的编译器中得到解决,例如 OpenJDK 7 。
它仅影响用作 final 字段初始化程序的前向引用。该问题似乎同样影响静态和非静态字段。
请注意,
call()
中对bar
的引用是合法的前向引用,因为它出现在不同的类中(请参见第 8.3 节中的示例)。 Java 语言规范 的 2.3)。另请注意,以下每个更改都会使错误消失:
使
bar
非最终:在静态或实例初始化程序块中初始化
bar
:移动
的初始化foo
到初始化块也有帮助。从对
foo
的非最终临时引用初始化bar
:使用
Test.foo
初始化bar
(由 Tom 发现) Anderson)或在非静态情况下使用this.foo
:删除
bar
并在call() 内使用
foo
引用该对象:Your understanding of forward reference is correct. The reference to
foo
on line 9 isn't a forward reference at all since it doesn't appear textually before its declaration (see the definition of what constitutes a forward reference in section 8.3.2.3 of The Java Language Specification).The behavior you observe is a symptom of a javac bug. See this bug report. The problem appears to be fixed in newer versions of the compiler, e.g. OpenJDK 7.
It only affects forward references used as initializers to final fields. The issue appears to affect static and non-static fields equally.
Note that the reference to
bar
incall()
is a legal forward reference since it occurs inside a different class (see examples in section 8.3.2.3 of The Java Language Specification).Also, note that each of the following alterations make the error disappear:
Making
bar
non-final:Initializing
bar
in static or instance initializer block:Moving the initialization of
foo
to an initializer block also helps.Initializing
bar
from a non-final temporary reference tofoo
:Initializing
bar
withTest.foo
(found by Tom Anderson) or withthis.foo
in non-static case:Removing
bar
and referring to the object usingfoo
insidecall()
:Java 语言规范 特别提到了对对象的限制初始化阶段期间的字段,具体而言(C 是接口或类):
满足以下条件时,会发生前向引用的编译时错误:
文章什么是前向引用规则?包含对初始化成员和前向引用时的规则和限制的精彩解释。
The Java Language Specifications specifically mentions restrictions on object fields during the initializations phase, specifically (C is an interface or class):
A compile-time error for forward references occurs when these conditions are fulfilled:
The article What are the forward reference rules? contains an excellent explanation of the rules and the restrictions when it comes to initializing members and forward referencing.