@Java 5代码中接口实现方法的重写注释不';不要给出编译错误 我有一个遗留Java 5项目,该项目将至少维持到2017年12月31日 我的默认系统JDK是1.8 还安装了JDK 1.5,如和中所述

@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

POM包含(如中所述):

就我所理解的Java+Maven的魔力而言,这应该是
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>