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

发布于 2024-12-17 07:38:51 字数 1055 浏览 4 评论 0原文

我正在尝试编译一个 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 的声明引用 foofoo 应该在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

层林尽染 2024-12-24 07:38:51

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

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

它仅影响用作 final 字段初始化程序的前向引用。该问题似乎同样影响静态和非静态字段。

请注意,call() 中对 bar 的引用是合法的前向引用,因为它出现在不同的类中(请参见第 8.3 节中的示例)。 Java 语言规范 的 2.3)。

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

使 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 初始化 bar(由 Tom 发现) Anderson)或在非静态情况下使用 this.foo

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;
    }   
});

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 in call() 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:

static Object bar = foo;

Initializing bar in static or instance initializer block:

static final Object bar;

static {
  bar = foo;
}

Moving the initialization of foo to an initializer block also helps.

Initializing bar from a non-final temporary reference to foo:

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

Initializing bar with Test.foo (found by Tom Anderson) or with this.foo in non-static case:

static final Object bar = Test.foo;

Removing bar and referring to the object using foo inside call():

static final Object foo = method(new java.util.concurrent.Callable<Object>() {
    @Override
    public Object call() throws Exception {
        return foo;
    }   
});
不气馁 2024-12-24 07:38:51

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

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

  • 该用法发生在 C 的实例(分别为静态)变量初始值设定项或 C 的实例(分别为静态)初始值设定项中。
  • 用法不在作业的左侧。
  • 通过一个简单的名称即可使用。
  • 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 usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
  • The usage is not on the left hand side of an assignment.
  • The usage is via a simple name.
  • C is the innermost class or interface enclosing the usage.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文