Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 258
TryMyBest Avatar asked Dec 05 '16 19:12

TryMyBest


People also ask

Does Maven work with Java 8?

2.1. The Maven compiler accepts this command with –target and –source versions. If we want to use the Java 8 language features, the –source should be set to 1.8. Also, for the compiled classes to be compatible with JVM 1.8, the –target value should be 1.8. The default value for both of them is the 1.6 version.

How do I change my default Maven compiler?

Show activity on this post. Create a pom-only ( <packaging>pom</packaging> ) project that has the compiler settings (and any other default settings) you want. You give treat it like any other project (release it; deploy it to your Maven repo, etc.). It doesn't help much if all you want to set is compiler settings.

Which Java compiler does Maven use?

The default Java compiler version used by Maven is Java 1.5 .

How do I change the default version of Maven in Java?

Maven JDK 1.8 use in Eclipse As soon as a new Maven build takes place, the JDK version reverts back to 1.5. The only way to make this a permanent change is to edit the Maven compiler plugin POM entry and force Eclipse and Maven to use Java 1.8.


1 Answers

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).
like image 142
Tunaki Avatar answered Oct 11 '22 12:10

Tunaki