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

时间:2011-11-22 22:17:41

标签: java javac forward-declaration

我正在尝试编译一个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;,编译器可以接受该类。

如何解释?在错误之后,我对转发的理解是意味着什么?或者这是一些我不知道的特殊情况?

2 个答案:

答案 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?包含对初始化成员和转发引用时规则和限制的出色解释。