Maven compiler plugin configured with Java 7 but s

2019-06-26 07:32发布

In my project, we are going to use Java 7 for maven-compiler-plugin and we assume that after Maven compile, all the code which is using Java 8 should not compile successfully.

However, in my case, there is a file using Arrays.stream(T[] array) which can be used from Java 8 and it still compiles successfully. Below is some of the pom.xml file which configure the Java version. Would you please have a look and give me any idea why my files can still compile successfully although I configure it to be Java 7?

For the pom.xml, I skip the dependencies and so on and only list the properties and the build.

<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> 

And for the file I use a method in Java 8, the line is something like this:

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

And what I want is that since I configure the Java version to be 7 in Maven and after compile, the file which use the Java 8 should not compile successfully and show the errors like "... are allowed only at source level 1.8 or above".

2条回答
Juvenile、少年°
2楼-- · 2019-06-26 08:18

The <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 generally misunderstood.

source does not instruct javac to compile the Java source files with the specified JDK version. It instructs javac to check the version of the accepted source code, which is very different. A major version of Java sometimes brings changes to the syntax of the source code. For example, in Java 1.4, you could not write source code containing generics, like List<String>; it wasn't valid. But with Java 5, you can, which means that a new kind of Java source code was now accepted by the JDK 5 compiler. A JDK 1.4 compiler, faced with a List<String>, can only error because it doesn't know that, when the JDK 5 compiler accepts it perfectly. Setting the -source 1.4 option would tell the JDK 5 compiler to interpret the source code as JDK 1.4 source code; therefore, if that code did contain generics, it would fail, because that source code isn't valid in that version. What this also means, is that if the source code doesn't contain any Java 5 specific source code, it would compile just fine with -source 1.4.

In the example here, you have a case where the javac compiler of JDK 8 is instructed to check the source code with regard to Java 7. And actually, the line

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

does not use any Java 8 specific source code. Sure, it uses Java 8 specific classes, but the source code itself would perfectly be understandable by a JDK 7 compiler.

  • There are no lambda expressions. Add a simple map(i -> i) in your pipeline, and then javac will error, telling you:

    lambda expressions are not supported in -source 1.7

    It detected that the source code used a specific feature that isn't available in the set of the JDK 7 source code features.

  • There are no invocations of static methods on interfaces. Replace your Stream pipeline with Stream.of(arg) instead of Arrays.stream(arg). This time, you'll get an error:

    static interface method invocations are not supported in -source 1.7

    Arrays is not an interface, so invoking a static method stream on that class is perfectly valid JDK 7 source code. However, Stream is an interface (which is, of course, known to the JDK 8 compiler you're using), and, before Java 8, interfaces couldn't contain static methods. As such, it isn't valid Java 7 source code.

There are more like that, but the point isn't to describe them all here (type annotations, repeated annotations, method references, intersection types in cast... you can see all of them in javac source code for example). All in all, there is no reason for javac to fail with that source code and the -source 7 option.

target is another beast entirely; this isn't the issue here, so suffice it to say that it instructs javac to generate byte code that targets the specified version the VM. It does not ensure in any way, that the byte code produced will actually run with said version of the VM. If you want to ensure that, the -bootclasspath option is to be used.


Coming back to the task at hand here, which is actually to make compilation fail here. With Maven, you have 2 solutions:

  • Fork the compiler, and set an executable pointing to the JDK 7 compiler.
  • Or (preferred), use the mechanism of toolchains, to ensure that every Maven plugins (aware of toolchains) use this JDK throughout the build (i.e. the Javadoc plugin for example, which uses the javadoc tool of the JDK installation).
查看更多
乱世女痞
3楼-- · 2019-06-26 08:21

I agree with everything that @Tunaki states. Here is an example of the configuration from maven documentation to help you if you decide to go with option one.

<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>
查看更多
登录 后发表回答