【问题标题】:Issue about java 8 backward compatibility: new methods in JDK关于 java 8 向后兼容性的问题:JDK 中的新方法
【发布时间】:2016-01-12 22:39:39
【问题描述】:

简单的问题。在 Java 8 中,我们在 JDK 类中有大量新方法。假设我们使用 Java 7(或 Java 6)创建了这样的类:

class MyArrayList<E> extends ArrayList<E> {
        public void sort(Comparator<E> c) {
            // some sort
        }
}

这是相当合理的实现。现在我们尝试用 Java 8 编译它并收到预期的编译错误:

error: name clash: sort(Comparator<E#1>) in MyArrayList and sort(Comparator<? super E#2>) in ArrayList have the same erasure, yet neither overrides the other
            public void sort(Comparator<E> c) {
                        ^   where E#1,E#2 are type-variables:
    E#1 extends Object declared in class I.MyArrayList
    E#2 extends Object declared in class ArrayList

在这里我想提出两个问题:

  1. 即使使用 JDK 8 使用 javac -source 1.7 -target 1.7 选项,我也会收到 same 错误 - 为什么?我认为这些选项应该允许编译遗留代码。

  2. 一般来说向后兼容性如何?

编辑确切地说,可能是我做错了什么? JDK 1.8.0_65,Mac OS X:

bash-3.2$ javac -version
javac 1.8.0_65
bash-3.2$ javac -source 1.7 -target 1.7 MyArrayList.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.7
MyArrayList.java:7: error: name clash: sort(Comparator<E#1>) in MyArrayList and sort(Comparator<? super E#2>) in ArrayList have the same erasure, yet neither overrides the other
    public void sort(Comparator<E> c) {
                ^
  where E#1,E#2 are type-variables:
    E#1 extends Object declared in class MyArrayList
    E#2 extends Object declared in class ArrayList
1 error
1 warning

【问题讨论】:

  • 这在 javac 文档中有描述。 “但 javac 也支持交叉编译,其中类是针对不同 Java 平台实现的引导和扩展类编译的。交叉编译时使用 -bootclasspath 和 -extdirs 选项很重要。”
  • -source 1.7 确保不使用 Java 8 的语言特性,-target 1.7 确保生成的字节码与 1.7 JVM 兼容。两者都不会更改正在使用的运行时库。您需要更多选项来在编译期间将运行时库更改为 Java 7。
  • @Tunaki 非常感谢。这是我第二个问题的答案。
  • 源和目标指定要使用的语言版本,而不是要使用的库版本。

标签: java java-8 backwards-compatibility


【解决方案1】:
  1. 因为即使使用了这些选项,您仍在针对 Java 8 类进行编译。 JDK 不知道在哪个版本的 JDK 中出现了哪些方法。所有这些选项所做的就是告诉编译器在您正在编译的代码中只接受 Java 7 语法,并生成 Java 7 字节码。您必须将实际链接传递给 JDK 7 类(使用 -bootclasspath 选项)才能进行交叉编译。

  2. 是的,这是个问题。不是很大,而且拥有所有这些新的默认方法的好处比拥有一些罕见的非编译代码的不便更重要。

【讨论】:

  • 这很奇怪 :) 如果我有 JDK 7,为什么我应该尝试用 JDK 8 编译它?看起来像没用的功能,不是吗?
  • 默认情况下我会这样做。除非我有一个可以与 Java 7 一起使用的库/框架,但在与 Java 8 一起使用时提供了附加值,或者必须支持仅存在于 Java 8 中的类。以 Spring 为例:它本机支持 Java 中的类。 time 包,但仍然与 Java 7 兼容。
  • @Andremoniy:当方法被添加到超类时,类演化的潜在问题在 Java 中一直存在。至少,Java 8 的 javac 现在警告在没有匹配引导类路径的情况下使用 -source/-target 选项。这种用法还是有道理的。 Java 8 编译器可能包含新的错误修复。有关差异的示例,请参阅 here
【解决方案2】:
  1. -source 1.7 只说源使用 Java 7 语言特性。 -target 1.7 表示输出的字节码针对特定版本的 JVM。但是,您仍在针对 JDK 8 进行编译。由于您是交叉编译,您必须使用-bootclasspath-extdirs 告诉javac Java 7 的引导程序和扩展类在哪里存在
  2. 接口中的Default methods(在 Java 8 中引入)允许您在不破坏现有代码的情况下添加新功能。这不是万无一失的解决方案,并且可能存在一些小问题(this answer 详细解释了这些问题)。但总的来说,向后兼容性问题很少见。

【讨论】:

  • 这很奇怪 :) 如果我有 JDK 7,为什么我应该尝试用 JDK 8 编译它?看起来像没用的功能,不是吗?
  • 不一定。这样,您可以只维护运行时库 jar 和您需要的任何其他扩展,而不是维护特定版本的 整个 JDK 分发。此外,您可以只使用一个编译器编译到不同的目标,而不必为您需要的目标在编译器版本之间切换(使工具更容易使用)。但是,是的,如果您已经拥有 JDK 7,那么您并不严格需要使用 JDK 8 来编译 JDK 7 代码。这取决于您的用例。
猜你喜欢
  • 1970-01-01
  • 2011-12-01
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多