为什么Eclipse可以让我将一些Java 7语言特性编译成Java 6类文件?

5
我发现在Eclipse(使用Eclipse编译器)中,我可以使用一些Java 7语言特性,但仍然创建Java 6类文件。在下面的图片中,您可以看到两个成功编译为Java 6类文件的Java 7语言特性。然而,其他被注释掉的Java 7特性则无法编译。
我的假设是Eclipse正在确定哪些Java 7语言特性与Java 6 JVM兼容,哪些不兼容。例如,通用类型JComboBox只是一个编译(而不是运行时)特性,因此我可以想象它如何兼容。然而,我认为字符串开关功能可能会在字节码上产生差异,并依赖于新的JVM功能,但我可能错了...
我的问题:
1. Eclipse是否真的聪明到知道哪些Java 7语言特性能够编译成Java 6类文件,哪些不能?
2. 下面的示例显然不是1.6源代码兼容的,那么为什么将“源兼容性”设置为1.6不会引起错误?
3. 这个“技巧”似乎让我使用至少一些Java 7语言特性并仍然创建Java 6类文件。使用具有源1.7和目标1.6的javac将失败,那么为什么这样做有效?Eclipse编译器是否具有javac没有的功能?
为了比较,当我切换到Java 6编译器时,结果如下所示。
4个回答

1
我不知道为什么Eclipse允许这种情况,或者这只是一个bug。1.7版的javac会告诉你。
error: strings in switch are not supported in -source 1.6

我也不知道为什么JComboBox能够工作,

System.out.println(new JComboBox<String>() {}.getClass().getGenericSuperclass());

> javax.swing.JComboBox<java.lang.String>

在运行时具有不应存在的通用信息。允许对非泛型类使用泛型应该被视为不兼容而被拒绝。虽然我没有在JVM6上运行上面的代码,但也许它甚至会崩溃。

但是至少switch在技术上没有问题。http://www.benf.org/other/cfr/java7switchonstring.html显示这只是一个编译器的技巧,不需要新的语言特性、API或字节码。

稍微简化的例子:

int java7(String string) {
    switch (string) {
        case "BB":
            return 12;
        case "FRED":
            return 13;
    }
    return 0;
}

变成了基本上

int java6(String string) {
    switch (string.hashCode()) {
        case 2112:
            if (string.equals("BB"))
                return 12;
            break;
        case 2166379:
            if (string.equals("FRED"))
                return 13;
            break;
    }
    return 0;
}

基于事实,String#hashCode()的结果是指定的且不得更改。编译器可以节省您编写合法代码所需的时间。
同样的情况也适用于钻石操作符:例如,new ArrayList<>() 可以被编译器简单地解析。
Android工具允许半Java 7兼容性,因此您可以使用它。然而,不同之处在于它们使用针对Java 7的.class文件。Android需要将.class文件转换为其内部格式,因此他们的.class到.dex编译器可以使用任何输入来生成可被Android运行时理解的指令。
例如,try-with-resource就无法工作,因为它需要AutoCloseable接口,而该接口在Java 6中不存在。
而像Lambda表达式这样的特殊功能确实需要新类型的字节码。

1
我认为有两件事情正在发生:
  1. 我怀疑第一行(使用通用的 JComboBox)能够正常工作,是因为链接了 Java 1.7 的 rt.jar 而不是 Java 1.6 的 rt.jar (我有一个使用 JavaSE-1.6 设置的项目,在这种情况下,即使使用您的第一组设置,该第一行也无法编译)。但这是一个类库问题,而不是语言版本问题。(即使使用 javac 编译 Java 应用程序时,如果针对比运行时版本更高的 rt.jar 进行编译,仍可能出现很多问题)。

  2. 第二行可能代表 Eclipse 编译器中的一个 bug。虽然大多数 Java 7 新语言功能可以纯粹在编译器中实现(自 2013 年后 Android 就这样做了),但这样做显然与 Java 6 不兼容。

因此,简而言之,您已经发现了至少一个(可能是)不寻常的 Eclipse 配置中的 bug。请小心并不要依赖它。


0
我的猜想是您对于ECJ为何在设置为Java 6时可以编译某些内容而不是其他内容的想法是正确的。泛型确实只是编译成与强制转换相同的东西,因此如果目标设置为Java 6,则可能是这个原因导致它可以工作?
请参见什么是javac和Eclipse编译器之间的区别?了解javac和ECJ之间的其他区别。

0
这可能是由于您项目的构建路径上配置的JRE系统库与您选择的兼容级别不匹配。通常情况下,您几乎总是希望在项目的编译器设置中选择“使用执行环境的兼容性”选项。检查您项目的构建路径,看看是否将JRE系统库指定为执行环境。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接