【问题标题】:Illegal forward reference error for static final fields静态最终字段的非法前向引用错误
【发布时间】:2011-11-22 22:17:40
【问题描述】:

我正在尝试编译一个被javac 拒绝并出现非法前向引用 错误的Java 类,其中违规引用在词法上 被引用的字段。以下类在显示相同行为的同时被尽可能地精简:

java.util.concurrent.CallableObject 的许多用途只是用作占位符来删除不相关的代码。

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;,编译器会接受该类。

如何解释?我对 forward 的理解是 lexically after 是错误的还是我不知道的一些特殊情况?

【问题讨论】:

  • 我可以确认 Sun 的 JDK 1.6.0_29 中的 javac 拒绝了这一点; Eclipse Indigo 毫无怨言地编译了它。您在 javac 或 Eclipse 中发现了一个错误 - 恭喜!
  • 注意可以去掉method这个方法,直接把新的Callable赋值给foo,同样报错。
  • 我认为这是 javac 中的一个错误,尽管我没有足够的语言律师来确定。我注意到你可以通过写bar = Test.foo来解决它。
  • gcj 4.4.3 也编译了这个。

标签: java javac forward-declaration


【解决方案1】:

您对前向引用的理解是正确的。第 9 行对foo 的引用根本不是一个前向引用,因为它在其声明之前没有以文本形式出现(请参阅构成前向引用的定义The Java Language Specification 的第 8.3.2.3 节中)。

您观察到的行为是 javac bug 的症状。见this bug report。该问题似乎在较新版本的编译器中得到修复,例如OpenJDK 7.

它只影响用作 final 字段的初始值设定项的前向引用。该问题似乎同样影响静态和非静态字段。

请注意,call() 中对bar 的引用是合法的前向引用,因为它出现在不同的类中(参见The Java Language Specification 的第8.3.2.3 节中的示例)。 p>

另外,请注意以下每一项更改都会使错误消失:

使bar 非最终结果:

static Object bar = foo;

在静态或实例初始化程序块中初始化bar

static final Object bar;

static {
  bar = foo;
}

foo 的初始化移动到初始化块也有帮助。

从对foo的非最终临时引用初始化bar

static Object tmp = foo;
static final Object bar = tmp;

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

static final Object bar = Test.foo;

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

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

【讨论】:

    【解决方案2】:

    Java Language Specifications 特别提到了在初始化阶段对对象字段的限制,特别是(C 是一个接口或类):

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

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

    What are the forward reference rules? 文章对初始化成员和前向引用的规则和限制做了很好的解释。

    【讨论】:

    • 我看不出这如何回答这个问题?对于赋值bar = foo,这些条件都满足了,这意味着foo 必须在bar 之前以文本形式声明——就是这样。另一方面,对于return bar的用法,最后一个条件不满足,因此对声明的顺序没有限制。
    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 2015-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多