javamaven-pluginmaven-compiler-plugin

maven-compiler-plugin source not work as expect


I have java11 on my local, and I am using maven-compiler-plugin setting it's source/target to 1.8, in my code I am using Objects.requireNonNullElse(null, new Object()); which is not support in java 8. When I run mvn package it works without any issue. I know the source param in maven-compiler-plugin specifies which java version you want to used to compile your source code, I am setting it to 1.8, but mvn package still compiles the source code successfully even I use some method not support in java 8. How could this happen? Thanks.

You can find my local settings here

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
$ java -version
openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment 18.9 (build 11.0.12+7)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.12+7, mixed mode)

Solution

  • There's a difference between language level (e.g. the existence or non-existence of lambdas) and the standard library. Your issue is with the latter.

    Maven (delegating to javac), will always use the standard library of whatever JDK you're using to compile, because how could it use anything else?

    Suppose you're trying to target Java 8, but you're compiling with JDK 17. Suppose you used a class which existed in Java 8 but was removed in Java 9. How is Java 17 supposed to handle that gracefully? It doesn't have that class, because it was deleted. How can it do type-checking against something that doesn't exist? It can't.

    Your case is the opposite: you're relying on a class which was introduced in a more recent version of Java than you're targeting, but fundamentally the issue is the same.

    Java really has no way to track which classes and methods were introduced at what stage, besides annotations which can't express the full set of possible changes (e.g. the delete case I already mentioned - you can't add an annotation to something that doesn't exist). To be able to properly support the kind of behaviour that you're expecting, the JDK would need knowledge of all signatures of all methods of all classes for all previous versions, and it doesn't have that.

    If this is an issue for you, and I could see why it would be, then you should compile with the a version of the JDK which matches the version you're actually targeting.

    There might be an additional plugin you could use, but if there is then I'm not aware of it. IntelliJ's inspections are aware of the API incompatibility, which is at least proof that's it's technically feasible to detect this programmatically.