5

我发现在 Eclipse 中(使用 Eclipse 编译器)我可以使用一些 Java 7 语言功能,但仍然可以创建 Java 6 类文件。在下图中,您可以看到成功编译为 Java 6 类文件的两个 Java 7 语言特性。但是,注释掉的其他 Java 7 特性不能编译。

我的假设是 Eclipse 正在确定哪些 Java 7 语言特性与 Java 6 JVM 兼容,哪些不兼容。例如,泛型类型 JComboBox 只是一个编译(而不是运行时)特性,所以我可以想象它会如何兼容。虽然我认为 switch String 功能可能会在字节码中产生差异并依赖于新的 JVM 功能,但我可能是错的......

我的问题:

  • Eclipse 真的足够聪明,可以知道哪些 Java 7 语言特性能够编译成 Java 6 类文件,哪些不能?

  • 下面的例子显然不兼容 1.6 源,那么为什么将“源兼容性”设置为 1.6 不会导致错误呢?

  • 这个“技巧”似乎让我至少可以使用一些 Java 7 语言功能,并且仍然可以创建 Java 6 类文件。将 javac 与源 1.7 和目标 1.6 一起使用会失败,那么为什么会这样呢?Ecilpse 编译器是否具有 javac 所没有的功能?

在此处输入图像描述

为了比较,这里是我切换到 Java 6 编译器时的结果,正如预期的那样。

在此处输入图像描述

4

4 回答 4

1

我不知道为什么 Eclipse 允许这样做,或者这是否只是一个错误。1.7javac会告诉你

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>

在运行时具有不应存在的通用信息。如果 IMO 因不兼容而被拒绝,则允许对非泛型类使用泛型。不过,我没有在 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<>()可以简单地由编译器解析。

允许相同的 semi 7 兼容性的 android 工具允许您使用它。但是,不同之处在于它们使用针对 Java 7 的.class 文件。无论如何,Android 都需要将 .class 文件转换为其内部格式,因此他们的 .class 到 .dex 编译器可以使用任何输入来生成 Android 运行时可以理解的指令。

例如 try-with-resource 将不起作用,因为它需要AutoCloseableJava 6 中不存在的接口等。

像 Lambda 表达式这样的特殊功能也确实需要新类型的字节码。

于 2014-10-30T17:37:14.323 回答
1

我认为有两件事正在发生:

  1. 我怀疑第一行(带有 generic JComboBox)可以工作,因为rt.jar链接的是 Java 1.7 而不是 Java 1.6 rt.jar(我有一个使用 JavaSE-1.6 设置的项目,在这种情况下,即使使用第一个组合,第一行也无法编译设置)。但这是一个类库问题,而不是语言版本问题。javac(即使你编译你的Java应用程序rt.jar比你运行它时更新的版本,你也会遇到很多问题)。

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

因此,简而言之,您在(可能)不寻常的 Eclipse 配置中发现了至少一个错误。小心,不要依赖它。

于 2014-10-30T15:47:11.880 回答
0

我的猜测是你是正确的,为什么当设置为 Java 6 时 ECJ 会编译一些东西而不是其他东西。泛型只是编译成与强制转换相同的东西,所以这可能就是为什么如果目标设置为 java 6 它可以工作的原因?

请参阅javac 和 Eclipse 编译器有什么区别?对于 javac 和 ECJ 之间的其他差异。

于 2014-10-30T15:23:33.470 回答
0

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

于 2014-10-30T17:14:16.303 回答