@Java 5代码中接口实现方法的重写注释不';不要给出编译错误 我有一个遗留Java 5项目,该项目将至少维持到2017年12月31日 我的默认系统JDK是1.8 还安装了JDK 1.5,如和中所述
POM包含(如中所述): 就我所理解的Java+Maven的魔力而言,这应该是@Java 5代码中接口实现方法的重写注释不';不要给出编译错误 我有一个遗留Java 5项目,该项目将至少维持到2017年12月31日 我的默认系统JDK是1.8 还安装了JDK 1.5,如和中所述,java,maven,cross-compiling,jdk1.5,Java,Maven,Cross Compiling,Jdk1.5,POM包含(如中所述): 就我所理解的Java+Maven的魔力而言,这应该是Maven编译器插件的有效咒语,以指示JDK1.8假装为JDK1.5并使用Java5引导类路径 根据,JDK 1.5将不允许@Override对接口的实现方法进行重写,只允许对超类中存在的重写方法进行重写 在@Override中,注释用于的实现方法,因此这是无效的Java 5代码: private static class DummyEvent implements PdfPTableEvent { @Ov
Maven编译器插件
的有效咒语,以指示JDK1.8假装为JDK1.5并使用Java5引导类路径
根据,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
注释的类上获得编译错误。我错过了什么
(已尝试:,但该插件仅在字节码处。)
编辑:这是我当前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>
有关更多详细信息,请参见下面的已接受答案。在使用
JDK 1.8
时,将目标设置为1.5
,并不保证您的代码能在1.5
上工作,如文档中所述
仅设置目标
选项并不能保证您的代码
实际在具有指定版本的JRE上运行。陷阱是
非故意使用仅存在于以后JRE中的API,这将
使代码在运行时因链接错误而失败。为了避免这种情况
问题,您可以将编译器的引导类路径配置为匹配
目标JRE或使用来验证
代码不使用非预期的API
问题的核心:Maven仍在使用启动它的JDK编译代码。因为您使用的是JDK 8,所以它是用JDK 8编译的,要用另一个编译器编译,您需要使用或指定正确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>
这看起来像是配置为使用JDK5的标准Maven项目。请注意,该类在实现接口的方法上使用了@Override
。这在Java6之前是不允许的
如果您尝试使用在JDK 8下运行的Maven构建此项目,它将编译,尽管设置为1.5
它为什么要编译?
Maven编译器插件没有错<代码>javac
是罪魁祸首。设置-source
标志不会告诉javac
使用此特定JDK版本编译项目。它指示javac
只接受特定版本的源代码。发件人:
-源代码版本
:指定接受的源代码版本
例如,如果指定了-source 1.4
,则您试图编译的源代码不能包含泛型,因为这些泛型是后来引入到语言中的。该选项强制应用程序的源代码兼容性。使用Java5泛型的Java应用程序与使用JDK4编译器的Java4程序在源代码上不兼容。同样,使用Java8Lambda表达式的应用程序与JDK6编译器的源代码不兼容
在本例中,@Override
是Java 5中已经存在的注释。但是,它的语义在Java6中发生了变化。因此,使用@Override
的代码,无论是否在实现接口的方法上,都与Java 5程序源代码兼容。因此,在这样的类上运行带有-source 1.5
的JDK 8不会失败
为什么它会跑?
转到第二个参数:target
。同样,这不是Maven编译器的问题,而是Java C的问题。当-source
标志强制源代码与旧版本兼容时,-target
强制二进制文件与旧版本兼容。此标志告诉javac
生成与旧版本JVM兼容的字节码。它不会告诉javac
检查编译后的代码是否可以在较旧的JVM版本中运行。为此,需要设置一个bootclasspath
,它将用指定的JDK交叉编译代码
显然,实现接口的方法上的@Override
不能在Java5VM上运行,因此javac
应该在这里使用。但是nope:has,这意味着编译完成后注释将被完全丢弃。这也意味着当交叉编译发生时,注释不再存在;在使用JDK 8编译时,它被丢弃。正如您所发现的,这也是为什么像这样的工具(使用预定义的JDK版本启用自动bootclasspath
)无法检测到这一点:注释丢失了
总之,您可以使用运行在JDK 8上的mvn clean package
打包上面的示例应用程序,并在Java 5 JVM上运行它,而不会遇到任何问题。它将打印“foo”
我怎样才能使它不被编译?
有两种可能的解决办法
第一个是直接的,通过编译器插件的属性:
<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>
缺少的部分是告诉那些插件工具链的配置在哪里。这是在toolschains.xml
文件中完成的,该文件通常位于~/.m2/toolschains.xml
中。从Maven 3.3.1开始,您可以使用--global toolschains
参数定义此文件的位置,但最好将其保存在用户主页中。内容将是:
<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
1.5
/usr/lib/jvm/jdk1.5.0_22
这声明了一个类型为jdk
的工具链,提供了一个jdk5和jdkhome的路径。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>
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");
}
}
<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>
<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>
<toolchain>
<type>jdk</type>
<provides>
<version>1.5</version>
</provides>
<configuration>
<jdkHome>/usr/lib/jvm/jdk1.5.0_22</jdkHome>
</configuration>
</toolchain>
</toolchains>