@Override对Java 5代码中实现的接口方法的注释没有给出编译错误
- 我有一个遗留的Java 5项目,至少在2017年12月31日之前仍然会保留。
- 我的默认系统JDK是1.8。
- 还安装了JDK 1.5,如在Ubuntu 12.04中安装Java 5和https://askubuntu.com/questions/522523/how-to-install-java-1-5-jdk-in-ubuntu中所述 。
POM包含(如https://stackoverflow.com/a/22398998/766786中所述 ):
compileWithJava5 ${env.JAVA5_HOME} ${java.5.home}/jre/lib ${java.5.libs}/rt.jar${path.separator}${java.5.libs}/jce.jar org.apache.maven.plugins maven-compiler-plugin 1.5 1.5 ${java.5.bootclasspath}
设置$JAVA5_HOME
:
• echo $JAVA5_HOME /usr/lib/jvm/jdk1.5.0_22
据我了解Java + Maven的魔力,这应该是maven-compiler-plugin
的有效咒语,指示JDK 1.8伪装成JDK 1.5并使用Java 5引导类路径。
根据为什么javac在@Override注释上失败 , JDK 1.5将不允许@Override
对接口的已实现方法允许,仅对超类中存在的重写方法允许。
在此提交中 , @Override
注释用于接口的已实现方法,因此这是无效的Java 5代码:
private static class DummyEvent implements PdfPTableEvent { @Override public void tableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) { } }
当我跑
mvn clean compile test-compile -P compileWithJava5
我没有在包含@Override
注释的类上获得编译错误。 我在这里想念的是什么?
(已经尝试过: Animal Sniffer Maven Plugin ,但该插件不会查看编译标志 ,只能查看字节代码。)
编辑 :这是我目前在我的POM中所拥有的。
compileWithLegacyJDK 1.5 ${env.JAVA5_HOME} ${java.home}/jre/lib ${java.libs}/rt.jar${path.separator}${java.libs}/jce.jar org.apache.maven.plugins maven-compiler-plugin 3.3 ${java.version} ${java.version} ${java.bootclasspath} ${java.version} true ${java.home}/bin/javac
运行
export JAVA5_HOME=/var/lib/jenkins/tools/hudson.model.JDK/1.5 mvn compile test-compile -P compileWithLegacyJDK
有关更多详细信息,请参阅下面的接
问题的核心:Maven仍然使用启动它的JDK编译代码。 由于您正在使用JDK 8,它正在使用JDK 8进行编译,并且要使用其他编译器进行编译,您需要使用工具链或指定正确JDK的路径。
建立
要测试此答案,您可以使用以下POM创建一个简单的Maven项目
4.0.0 test test 1.0-SNAPSHOT maven-compiler-plugin 3.3 1.5 1.5 /usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar
用一个类编译坐在src/main/java/test
,是:
package test; interface I { void foo(); } public class Main implements I { public static void main(String[] args) { new Main().foo(); } @Override public void foo() { System.out.println("foo"); } }
这看起来像是一个配置为使用JDK 5的标准Maven项目。请注意,该类在实现接口的方法上使用@Override
。 在Java 6之前不允许这样做。
如果你尝试使用在JDK 8下运行的Maven构建这个项目,它将编译,尽管设置1.5
。
为什么要编译?
Maven编译器插件没有错。 javac
是罪魁祸首。 设置-source
标志不会告诉javac
使用此特定JDK版本编译项目。 它指示javac
仅接受特定版本的源代码。 来自javac
文档 :
-source release
:指定接受的源代码版本。
例如,如果您指定了-source 1.4
,那么您尝试编译的源代码不能包含generics,因为稍后会将这些内容引入该语言。 该选项强制应用程序的源兼容性。 使用Java 5generics的Java应用程序与使用JDK 4编译器的Java 4程序不兼容。 同样,使用Java 8 lambda表达式的应用程序与JDK 6编译器不兼容。
在这种情况下, @Override
是Java 5中已经存在的注释。但是,它的语义在Java 6中发生了变化。因此,使用@Override
代码,无论是否在实现接口的方法上,都与源代码兼容Java 5程序。 因此,在这样的类上运行带有-source 1.5
的JDK 8不会失败。
它为什么运行?
在第二个参数: target
。 同样,这不是Maven编译器的关注点,而是javac
关注点。 虽然-source
标志强制与旧版本的源兼容性,但-target
强制与旧版本的二进制兼容性。 该标志告诉javac
生成与旧JVM版本兼容的字节代码。 它没有告诉javac
检查编译的代码是否可以实际运行旧的JVM版本。 为此,您需要设置一个bootclasspath
,它将使用指定的JDK交叉编译您的代码。
显然,实现接口的方法上的@Override
无法在Java 5 VM上运行,因此javac
应该在这里javac
。 但是nope: Override
有源保留 ,这意味着在编译完成后完全放弃了注释。 这也意味着当交叉编译发生时,注释不再存在; 在使用JDK 8进行编译时,它被丢弃了。正如您所知,这也是Animal Sniffer Plugin (它支持具有预定义JDK版本的自动bootclasspath
)之类的工具无法检测到这一点的原因:缺少注释。
总之,您可以使用在JDK 8上运行的mvn clean package
上面的示例应用程序,并运行它而不会在Java 5 JVM上遇到任何问题。 它将打印"foo"
。
我怎么能让它不编译?
有两种可能的解决方案。
第一个是直接的,通过Compiler Plugin的executable
属性指定javac
的路径 :
maven-compiler-plugin 3.3 1.5 1.5 /usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar 1.5 true /usr/lib/jvm/jdk1.5.0_22/bin/javac
这将设置编译器应与compilerVersion
参数一起使用的JDK的实际版本。 这是一种简单的方法,但请注意,它只会更改用于编译的JDK版本。 Maven仍将使用启动它的JDK 8安装来生成Javadoc或运行unit testing,或任何需要JDK安装工具的步骤。
第二种全球方法是使用工具链。 这些将指示Maven使用不同于用于启动mvn
的JDK,并且每个Maven插件(或任何知道工具链的插件)将使用此JDK来执行其操作。 编辑您的POM文件以添加maven-toolchains-plugin
的以下插件配置:
maven-toolchains-plugin 1.1 toolchain 1.5
缺少的成分告诉那些插件,该工具链的配置在哪里。 这是在toolchains.xml
文件中完成的,该文件通常位于~/.m2/toolchains.xml
。 从Maven 3.3.1开始,您可以使用--global-toolchains
参数定义此文件的位置,但最好将其保留在用户主目录中。 内容如下:
jdk 1.5 /usr/lib/jvm/jdk1.5.0_22
这声明了一个jdk
类型的工具链,它为JDK 5提供了JDK home的路径。 Maven插件现在将使用此JDK。 实际上,它也将是编译源代码时使用的JDK。
如果您尝试使用此添加的配置再次编译上面的示例项目…您将最终出现错误:
方法不会覆盖其超类中的方法
当您使用JDK 1.8
时,将target
设置为1.5
并不能保证您的代码可以在1.5
如maven-compiler-plugin
的文档中所述。
仅设置
target
选项并不能保证您的代码实际上在具有指定版本的JRE上运行。 缺点是无意中使用的API只存在于以后的JRE中,这会使您的代码在运行时因链接错误而失败。 要避免此问题,可以配置编译器的引导类路径以匹配目标JRE,也可以使用Animal Sniffer Maven插件validation代码是否使用非预期的API。