首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >@重写Java 5代码中实现的接口方法的注释不会产生编译错误

@重写Java 5代码中实现的接口方法的注释不会产生编译错误
EN

Stack Overflow用户
提问于 2016-10-14 12:12:06
回答 2查看 1.6K关注 0票数 3

POM包含(如https://stackoverflow.com/a/22398998/766786中所述):

代码语言:javascript
复制
<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

代码语言:javascript
复制
• echo $JAVA5_HOME
/usr/lib/jvm/jdk1.5.0_22

就我所理解的Java+Maven的魔力而言,这应该是maven-compiler-plugin的一个有效咒语,它指示JDK1.8假装为JDK1.5并使用Java5引导类路径。

根据为什么javac在@重写注释上失败JDK1.5将不允许在接口的实现方法上使用@Override,只允许在超类中存在的重写方法上使用@Override

此承诺中,@Override注释用于接口的实现方法,因此这是无效的Java5代码:

代码语言:javascript
复制
private static class DummyEvent implements PdfPTableEvent {

    @Override
    public void tableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
    }
}

当我跑的时候

代码语言:javascript
复制
mvn clean compile test-compile -P compileWithJava5

包含@Override注释的类没有编译错误。我在这里错过了什么?

(已经尝试过:动物嗅探器Maven插件,但是插件不查看编译标志,只在字节码上。)

编辑:这是我目前在POM中拥有的内容。

代码语言:javascript
复制
<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>

同跑

代码语言:javascript
复制
export JAVA5_HOME=/var/lib/jenkins/tools/hudson.model.JDK/1.5
mvn compile test-compile -P compileWithLegacyJDK

有关更多细节,请参见下面接受的答案。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-18 22:03:28

问题的核心: Maven仍然在用启动它的JDK编译代码。由于您使用的是JDK 8,所以它使用JDK 8进行编译,要使用另一个编译器进行编译,您需要使用工具链或指定正确的JDK路径。

设置

要测试这个答案,您可以使用以下POM进行一个简单的Maven项目

代码语言:javascript
复制
<?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下有一个要编译的类,即:

代码语言:javascript
复制
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只接受特定版本的源代码。来自文档

-source release:指定接受的源代码版本。

例如,如果您指定了-source 1.4,那么您要编译的源代码就不能包含泛型,因为这些泛型是后来引入到语言中的。该选项强制执行应用程序的源兼容性。使用Java 5泛型的Java应用程序与使用JDK 4编译器的Java 4程序不兼容。同样,使用Java 8 lambda表达式的应用程序与JDK 6编译器不兼容源代码。

在这种情况下,@Override是Java 5中已经存在的注释。然而,它的语义在Java6中发生了变化。因此,使用@Override的代码,无论它是否在实现接口的方法上,都是与Java5程序兼容的源代码。因此,在这样的类上使用-source 1.5运行JDK 8不会失败。

为什么要跑?

转到第二个参数:target。同样,这不是Maven编译器的问题,而是javac的问题。-source标志强制源代码与旧版本兼容,而-target则强制执行与旧版本的二进制兼容性。此标志告诉javac生成与较旧版本的JVM兼容的字节代码。它没有告诉javac检查编译后的代码是否真的可以与旧版本的JVM一起运行。为此,您需要设置一个bootclasspath,它将将您的代码与指定的JDK交叉编译。

显然,实现接口的方法上的@Override不能在Java5VM上运行,所以javac应该在这里运行。但是,nope:Override源保留,这意味着在编译发生后注释将被完全丢弃。这也意味着当交叉编译发生时,注释不再存在;它在用JDK 8编译时被丢弃了。正如您所发现的,这也是为什么像动物嗅探插件这样的工具(它支持带有预定义JDK版本的自动bootclasspath )不会检测到这一点:注释丢失了。

总之,您可以将上面的示例应用程序打包为运行在JDK 8上的mvn clean package,并运行它,而不会碰到Java5JVM上的任何问题。它将打印"foo"

我怎么能使它不编译呢?

有两种可能的解决办法。

第一个,直接的javac,通过编译器插件的executable属性:

代码语言:javascript
复制
<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,然后每个Maven插件(或任何知道工具链的插件)都将使用此JDK来执行它们的操作。编辑POM文件以添加maven-toolchains-plugin的以下插件配置

代码语言:javascript
复制
<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参数定义该文件的位置,但最好将其保存在用户家中。内容如下:

代码语言:javascript
复制
<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。

如果您试图再次编译上面添加的配置的示例项目.您最终会遇到错误:

方法不覆盖其超类中的方法。

票数 7
EN

Stack Overflow用户

发布于 2016-10-14 12:17:28

当您使用target时,将JDK 1.8设置为1.5并不能保证您的代码将在1.5上工作,这在maven-compiler-plugin的文档中已经解释过了。

仅仅设置target选项并不能保证您的代码在具有指定版本的JRE上实际运行。陷阱是仅存在于以后的JRE中的API的意外使用,这会使您的代码在运行时出现链接错误而失败。为了避免这个问题,您可以配置编译器的引导类路径来匹配目标JRE,或者使用动物嗅探器Maven插件来验证您的代码没有使用意外的API。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40042878

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档