Maven 编译器插件配置 Java 7 但仍然编译 Java 8 代码

Maven compiler plugin configured with Java 7 but still compiles Java 8 code

在我的项目中,我们将对 maven-compiler-plugin 使用 Java 7 并且我们假设在 Maven 编译之后,所有使用 Java 8 的代码都不会编译成功.

但是,在我的例子中,有一个使用 Arrays.stream(T[] array) 的文件可以从 Java 8 开始使用,它仍然可以成功编译。下面是配置 Java 版本的一些 pom.xml 文件。请你看一下,告诉我为什么我的文件仍然可以编译成功,尽管我将它配置为 Java 7?

对于 pom.xml,我跳过依赖项等,仅列出属性和构建。

<properties>
    <java.version>1.7</java.version>
</properties>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build> 

对于我使用 Java 8 中的方法的文件,该行是这样的:

buffer.append(Arrays.stream(arg).collect(Collectors.joining("/")));

而我想要的是,由于我在 Maven 中将 Java 版本配置为 7,并且在编译后,使用 Java 8 的文件不应成功编译并显示如下错误“...只允许在源代码级别 1.8 或更高版本”。

<source> and <target> flags of the Compiler Plugin, which maps directly to the -source and -target options of the Java compiler javac (when it is the one used), are .

source 不指示 javac 使用指定的 JDK 版本编译 Java 源文件。它指示 javac 检查接受的源代码的版本,这是非常不同的。 Java 的主要版本有时会更改源代码的语法。例如,在 Java 1.4 中,您不能编写包含泛型的源代码,例如 List<String>;这是无效的。但是使用 Java 5,你可以,这意味着 JDK 5 编译器现在接受了一种新的 Java 源代码。 JDK 1.4 编译器面对 List<String> 时只能出错,因为它不知道 JDK 5 编译器可以完美地接受它。设置 -source 1.4 选项会告诉 JDK 5 编译器将源代码解释为 JDK 1.4 源代码;因此,如果该代码确实包含泛型,它将失败,因为该源代码在该版本中无效。这也意味着,如果源代码不包含任何 Java 5 特定的源代码,它会用 -source 1.4.

编译得很好

在这里的例子中,JDK 8 的 javac 编译器被指示检查关于 Java 7 的源代码。实际上,行

buffer.append(Arrays.stream(arg).collect(Collectors.joining("/")));

不使用任何 Java8 特定的源代码。当然,它使用 Java 8 特定的 classes,但是源代码本身完全可以被 JDK 7 编译器理解。

  • 没有 lambda 表达式。在你的pipeline中添加一个简单的map(i -> i),然后javac就会报错,告诉你:

    lambda expressions are not supported in -source 1.7

    它检测到源代码使用了 JDK 7 个源代码功能集中不可用的特定功能。

  • 接口上没有调用静态方法。用 Stream.of(arg) 而不是 Arrays.stream(arg) 替换您的 Stream 管道。这一次,你会得到一个错误:

    static interface method invocations are not supported in -source 1.7

    Arrays 不是接口,因此在 class 上调用静态方法 stream 是完全有效的 JDK 7 源代码。但是,Stream 是一个接口(当然,您正在使用的 JDK 8 编译器知道它),并且在 Java 8 之前,接口不能包含静态方法.因此,它是无效的 Java 7 源代码。

还有更多类似的,但重点不是在这里描述它们 (type annotations, repeated annotations, method references, intersection types in cast... you can see all of them in javac source code for example)。总而言之,javac 没有理由因该源代码和 -source 7 选项而失败。

target 完全是另一种野兽;这不是这里的问题,所以只要说它指示 javac 生成针对指定版本 VM 的字节码就足够了。它不能以任何方式确保生成的字节码实际上会 运行 与所述 VM 版本。如果要确保这一点,则使用 -bootclasspath 选项。


言归正传,这里其实就是让编译失败。使用 Maven,:

  • Fork the compiler, and set an executable 指向 JDK 7 编译器。
  • 或者(首选),使用 toolchains 的机制,以确保每个 Maven 插件(知道工具链)在整个构建过程中使用此 JDK(即 Javadoc例如插件,它使用 JDK 安装的 javadoc 工具。

我同意@Tunaki 所说的一切。这是 maven 文档中的配置示例,如果您决定使用选项一,它可以为您提供帮助。

<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
      <verbose>true</verbose>
      <fork>true</fork>
      <executable><!-- path-to-javac --></executable>
      <compilerVersion>1.7</compilerVersion>
      <source>${java.version}</source>
      <target>${java.version}</target>
    </configuration>
  </plugin>
</plugins>