How to produce code in Java 11, but target Java 8 and above?

While conversion of classes compiled for JDK 11 to JDK 8 would be theoretically possible with a sophisticated tool, it’s not trivial. There are significant changes on the binary level.

First, JDK 11 introduced nest types, which eliminates the need to generate synthetic accessor methods when accessing private members of inner/outer classes. Of course, such access would fail in older versions.

It also introduced dynamic constants, though I don’t know whether the Java language exploits that feature anywhere. This is mainly intended for future versions.

Then, since JDK 9, string concatenation gets compiled using invokedynamic referring to java.lang.invoke.StringConcatFactory which is not present in Java 8.

A feature that could work, is private methods in interfaces, introduced in Java 9 as a language feature, but already handled on the binary level in Java 8.

Java 8 would also be unable to process module definitions, but I suppose, they would be ignored.


No, you cannot compile Java 11 source to Java 8 binaries.

In javac terms, the -source parameter can't be greater than the -target parameter.

So, if you want to produce Java 8 binaries, your sources should be written in java 8 (or earlier). If you don't use any Java 11 language features, your sources basically already are in Java 8 so this shouldn't be too big a problem.

Note that you can still use a JDK 11 to compile the Java 8 source into Java 8 binaries. The JDK version can be greater than the source and/or target versions.

Note: the javac documentation says nothing about the -source parameter having to be less than or equal to the -target parameter. There's lots of non-official documentation, however. For example, https://stackoverflow.com/a/9261298/691074

As far as I can tell there's also not a single counter-example of actually getting such a situation to work.


I could be wrong here, but at least up to now, javac isn't meant to be used in that way.

A bit of guessing here: you could try to see if --release 8 --target 8 works (without giving the --source 11 parameter).

But I doubt that this will work. I think there is no support in javac to accept N source code features, and have that compiled backwards to earlier target versions.

Of course, the compiler could have knowledge about the required transformations to turn N source code into (N-m) byte code. But that would make the compiler much more complex, and each release would add to that. It would also add dramatical cost to the testing efforts. I doubt that the compiler maintainers are willing to buy into that. It is really not like this is a widespread use case.

So, the only "solution" I know: branching, and double maintenance. And in order to keep things reasonable, I would simply keep a Java 8 version, and maybe one for Java 11.