POM包含(如https://stackoverflow.com/a/22398998/766786中所述):
<profile>
<id>compileWithJava5</id>
<!--
NOTE
Make sure to set the environment variable JAVA5_HOME
to your JDK 1.5 HOME when using this profile.
-->
<properties>
<java.5.home>${env.JAVA5_HOME}</java.5.home>
<java.5.libs>${java.5.home}/jre/lib</java.5.libs>
<java.5.bootclasspath>${java.5.libs}/rt.jar${path.separator}${java.5.libs}/jce.jar</java.5.bootclasspath>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>${java.5.bootclasspath}</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>
设置了
$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引导类路径。根据Why is javac failing on @Override annotation的规定, JDK 1.5 不允许在接口的已实现方法上使用
@Override
,而仅允许使用超类中存在的重写方法。在this commit中,
@Override
批注用于interface的已实现方法,因此这是无效的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,但是该插件doesn't look at compilation flags,仅在字节码处有效。)
编辑:这是我当前在POM中拥有的。
<profile>
<id>compileWithLegacyJDK</id>
<!--
NOTE
Make sure to set the environment variable JAVA5_HOME
to your JDK 1.5 HOME when using this profile.
-->
<properties>
<java.version>1.5</java.version>
<java.home>${env.JAVA5_HOME}</java.home>
<java.libs>${java.home}/jre/lib</java.libs>
<java.bootclasspath>${java.libs}/rt.jar${path.separator}${java.libs}/jce.jar</java.bootclasspath>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArguments>
<bootclasspath>${java.bootclasspath}</bootclasspath>
</compilerArguments>
<compilerVersion>${java.version}</compilerVersion>
<fork>true</fork>
<executable>${java.home}/bin/javac</executable>
</configuration>
</plugin>
</plugins>
</build>
</profile>
与运行
export JAVA5_HOME=/var/lib/jenkins/tools/hudson.model.JDK/1.5
mvn compile test-compile -P compileWithLegacyJDK
有关更多详细信息,请参见下面的接受的答案。
最佳答案
问题的核心:Maven仍使用启动它的JDK来编译代码。由于您正在使用JDK 8,因此它是与JDK 8一起编译的,并且要与其他编译器一起编译,因此您需要使用toolchains或指定正确JDK的路径。
建立
要测试此答案,您可以使用一个带有以下POM的简单Maven项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
有一个要编译的类,位于
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来构建该项目,则尽管设置了
<source>1.5</source>
,它仍将编译。为什么要编译?
Maven编译器插件没有错误。
javac
值得指责。设置-source
标志不会告诉javac
使用此特定的JDK版本编译您的项目。它指示javac
仅接受特定版本的源代码。从 javac
documentation:-source release
:指定接受的源代码的版本。例如,如果您指定了
-source 1.4
,那么您尝试编译的源代码就不能包含泛型,因为这些泛型是后来引入该语言的。该选项可增强应用程序的源兼容性。使用Java 5泛型的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
应该在此处吠叫。但是没有: Override
具有source retention,这意味着注释在编译发生后将被完全丢弃。这也意味着当交叉编译发生时,注释不再存在。使用JDK 8进行编译时,它被丢弃了。正如您所发现的,这也是为什么Animal Sniffer Plugin之类的工具(启用带有预定义JDK版本的自动bootclasspath
)不会检测到这一点的原因:缺少注释。总之,您可以使用在JDK 8上运行的
mvn clean package
打包上面的示例应用程序,然后运行它,而不会在Java 5 JVM上遇到任何问题。它将打印"foo"
。我怎样才能使其不被编译?
有两种可能的解决方案。
第一个直接的is to specify the path to
javac
通过Compiler插件的 executable
属性:<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
<compilerVersion>1.5</compilerVersion>
<fork>true</fork>
<!-- better to have that in a property in the settings, or an environment variable -->
<executable>/usr/lib/jvm/jdk1.5.0_22/bin/javac</executable>
</configuration>
</plugin>
这将使用
compilerVersion
参数设置编译器应使用的JDK的实际版本。这是一种简单的方法,但是请注意,它仅更改用于编译的JDK版本。 Maven仍将使用启动JDK 8的安装来生成Javadoc或运行单元测试,或者执行需要使用JDK安装工具的任何步骤。第二种全局方法是使用工具链。这些将指示Maven使用与用于启动
mvn
的JDK不同的JDK,然后每个Maven插件(或任何支持工具链的插件)都将使用此JDK执行其操作。编辑您的POM文件以添加以下 maven-toolchains-plugin
插件配置:<plugin>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>1.5</version>
</jdk>
</toolchains>
</configuration>
</plugin>
缺少的成分是告诉那些插件该工具链的配置在哪里。这是在
toolchains.xml
文件中完成的,该文件通常在~/.m2/toolchains.xml
中。从Maven 3.3.1开始,可以使用--global-toolchains
参数定义此文件的位置,但最好将其保存在用户主目录中。内容将是:<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.5</version>
</provides>
<configuration>
<jdkHome>/usr/lib/jvm/jdk1.5.0_22</jdkHome>
</configuration>
</toolchain>
</toolchains>
这声明了
jdk
类型的工具链,为JDK 5提供了到JDK主页的路径。 Maven插件现在将使用此JDK。实际上,它也是编译源代码时使用的JDK。而且,如果您尝试使用此添加的配置再次编译上面的示例项目,您将最终得到以下错误:
方法不会覆盖其超类中的方法
关于java - Java 5代码中接口(interface)的已实现方法的@Override注释未给出编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40042878/