12

正如这里所讨论的,javac 和其他 Java 编译器可以为if条件是“常量表达式”的语句提供代码消除功能。

如果我的代码使用依赖于不同包中定义的其他常量表达式的常量表达式,这会受到怎样的影响?

例如,假设我在各自指定的包中有以下类:

package foo;

public class Foo {
    public static final boolean CONDITION = false;
}

package bar;

import foo.Foo;

public class Bar {
    public void test() {
        if (Foo.CONDITION) {
            System.out.println("This line of code could be eliminated.");
        } else {
            System.out.println("This line of code will be executed.");
        }
    }
}

显然,如果foo-package 在运行时从外部 jar 文件加载,编译器在技术上不能仅仅假设这Foo.CONDITION将是错误的并且不应该消除-statementtrueif-branch。

然而,如果FooBar实际上在同一个包中,则true-branch 肯定应该被消除(如果编译器完全支持代码消除)。

不太确定如何最好地表达这个问题,但是:对于常量表达式 inFoo需要多“接近”才能被认为是常量 in ?他们需要在同一个文件中吗?同一个包?同一个jar文件?还是根本不重要(即编译器是否总是将其视为常量并在编译时使用在构建路径中找到的值)?BarFooBarFoo.CONDITION

4

2 回答 2

9

由于它是引用类的公共“签名”的一部分,因此假定它是常量。本质上,这个想法final意味着最终,如果你改变它,那是你的错。

这类似于引用类的实际方法签名,在编译时也按原样使用。这就是为什么如果您针对一个库版本编译代码并针对另一个版本运行它,您可能会得到一个NoSuchMethodError.

更新:实际上 JLS提供了更强有力的保证

如果字段是常量变量(第 4.12.4 节),则删除关键字 final 或更改其值不会因导致它们不运行而破坏与预先存在的二进制文件的兼容性,但它们不会看到任何新的使用值除非它们被重新编译。即使用法本身不是编译时常量表达式(第 15.28 节)也是如此。

如第 14.21 节末尾所讨论的,此结果是支持条件编译的决定的副作用。

于 2014-05-22T21:09:50.937 回答
4

你的常数有多接近并不重要。如果它是规范中定义的编译时常量,它将被内联。静态 final(常量)是根据 Java 规范内联的(尽管我也没有找到规范)。 这篇java world 文章详细讨论了该主题。相关的东西:

根据 Java 语言规范,使用可以在编译时评估的表达式初始化的任何静态 final 字段都必须编译为“内联”字段值的字节码。也就是说,在 Main 类中不会出现动态链接,告诉它在运行时从 InterfaceA 获取 A 的值。相反,文字 1 将直接编译成 Main.main()。

于 2014-05-22T21:13:34.477 回答