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

java 11 compatibility with java 8
java 8 backwards compatibility java 7
java 6 to java 8 upgrade issues
sourcecompatibility java 11
should i learn java 8 or 11
java 8 vs java 13
java 12 vs java 8
java 9 vs java 8

I am working on a small library and for obvious reasons I would like to produce code using all the Java 11 features (except modules I guess for now), but I would like the library to be compatible with Java 8 and above.

When I try this:

javac -source 11 -target 1.8 App.java

I get the following message:

warning: source release 11 requires target release 11

...and when I look at the byte code I see that the version of the class is 0x37 (Java 11):

$ xxd App.class
00000000: cafe babe 0000 0037 ...

And Java 8 cannot load it:

Exception in thread "main" java.lang.UnsupportedClassVersionError: App has been
    compiled by a more recent version of the Java Runtime (class file version 55.0),
    this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

How do people provide such compatibility? I am open to all build tools.

For me it seems easy just to transform high-level language (Java) into low-level (bytecode). It appears to me that when the high-level language changes, the low-level should stay the same. That is why I thought it was possible.

UPDATE

Guys, I don't think that this answer duplicates Move to OpenJDK-11 but compile in Java 8, because there the OP asks how to keep producing the code with Java 8 features, but target Java 11 (which is just a famous backward compatibility). My question is the other way around: I want to produce the code in Java 11, but target Java 8. I came across that question when I was researching the topic before posing the question. I didn't find it applicable to my situation.

The other question Can Java 8 code be compiled to run on Java 7 JVM does look similar to my question, but it was asked in 2013 and the bytecode obviously changed between Java 7 and Java 8.

I didn't think the bytecode changed that much since Java 8 that is why I asked this question.

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.

Migrating to Java 11 while maintaining a Java 8 client library, We have been big adopters of Java 8 features, including lambda… It shouldn't require so many characters to create a list of one element! using Java 8 code while running Java 11), but unfortunately we found that the inverse option — release <version> which cross-compiles to an older Java version. And that’s definitely high time to make up for it, because during this time we already had Java 9 release, which had been quickly superseded by Java 10 and there is Java 11 release just around the corner, which is going to be an important release, because similarly to Java 8, it will be a long-term-support version.

Although I didn't see anything explicit in the javadoc for javac, I think you can only state the same version for both -source and -target options. Java 11 features are not supported in Java 8 although the opposite is true, a higher java version can run code compiled in a lower version. So I don't think it is possible to compile code written to Java 11 to be runnable in Java 8.

Transition from Java 8 to Java 11, How to make existing code modular is not covered here. You can continue to use internal API in Java 11, but replacing the usage should be a priority. run these tools over existing jars and class files, including third-party libraries. The --add-exports option allows the target module to access the public  Java 11 is supported by Maven by using at least the 3.8.0 version of the Maven Compiler Plugin. As you are probably still using Java 8 for the majority of your projects, you don't want to modify the JDK Maven is using just to test out Java 11. You can use the Maven Toolchains Plugin for this purpose:

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.

Compatibility Guide for JDK 8, Source: Source compatibility concerns translating Java source code into class Version 52.0 class files produced by a Java SE 8 compiler cannot be used in In Java SE 8 and above, the Java Virtual Machine considers the The combination of options " -source 1.4 -target 1.5 " will use those newer idioms, but also output  However, some of the Java 11 changes are outside the scope of the tools and could not be detected, such as certificate changes and other JVM options that were removed. The tool attempts to address application-related issues only, but if you’re curious about all the changes that went into Java 11, take a look at Oracle’s JDK 11 Migration Guide.

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.

Cannot compile to Java 8 with Java 11 source · Issue #8009 · gradle , I try to compile to Java 8 from Java 11 source by setting the GitHub is home to over 50 million developers working together to host and review code, manage projects, warning: source release 11 requires target release 11 * Try: Run with But javac also supports cross-compiling, in which classes are  % javac -source 1.6 -target 1.5 The above code conveys the message that, the code base will be compiled with the Java 1.6 version, but the targeted minimum environment support is Java 1.5. In other words, converted class files are compatible to with 1.6 JVM. Ant – Source and Target

There's a tool called Jabel created by @bsideup, which allows you to do this. It pretends to be an annotation processor so it could be plugged into javac. However, it doesn't do any actual annotation processing. Instead, during the compilation, it hacks into javac internals and makes it believe that newer features like var, diamond in anonymous class creation and even newer ones like text blocks and switch expressions are ok to use in Java 8.

This is hacky solution without any warranty so use with caution.

From Java 8 to Java 11, You do not have to modularise your code to upgrade to Java 11. Some projects have been able to keep up. But that is because as a library author I have to produce a jar file with module-info that builds and runs on Java 8,  The JDK 11 release includes an implementation of the Transport Layer Security (TLS) 1.3 specification ( RFC 8446 ). TLS 1.3 is the latest iteration (August 2018) of the Transport Layer Security (TLS) protocol and is enabled by default in JDK 11.

How To Use Multi-release JARs To Target Multiple Java Versions , Java 8 and older load version-unspecific class files; Java 9 and newer load version-specific Use the jar option --release to create MR-JARs. Java annotations are used to provide meta data for your Java code. Being meta data, Java annotations do not directly affect the execution of your code, although some types of annotations can actually be used for that purpose. Java annotations were added to Java from Java 5. This text covers Java annotations as they look in Java 8, Java 9 and

All You Need To Know For Migrating To Java 11, With the challenges of migrating from Java 8 onto a modular and Now the code bases have to follow and many projects will move from Java 8 directly to Java 11. This may seem like boring stuff, but the six-month release cycle, the Oracle worked hard to make Oracle JDK 11 and OpenJDK 11 almost  Configure plugins for Java 11. The most important plugins for Java 11 are the compiler plugin, surefire (for unit-tests) and failsafe (for integration-tests). In order to compile your project for Java 11 add the release configuration to the compiler plugin, a new compiler parameter to replace the source and target version parameters:

Upgrading from Java 8 to Java 12, APIs that are not core to the JDK have also been removed in Java 11. Once "​over the hump" of this first upgrade, it's worth at least testing the application having to adopt new versions of Java in our production application code. But now that Java 11 has replaced Java 8 as the latest LTS, and now that  <properties> <java.version>1.9</java.version> </properties> By setting the java.version property we declare that the source and the target Java versions are both equal to 1.9. Above all, we should keep in mind that this property is a Spring Boot Specification. Additionally, starting from the Spring Boot 2.0, Java 8 is the minimum version.

Comments
  • If you want to target and build a java 8 version of your project, you can only use java 8 language features and libraries, not the newer ones.
  • Which Java 11 language features do you need? The differences between Java 8 and Java 11 are minor.
  • Thank you for your answer
  • I think: it is not possible with the tools we have right now (most likely). If you spend enough resources, you probably could create such a compiler. It's just that this might be really expensive, and not much people interested in that feature.
  • @GhostCat There are major differences in the class-file format. E.g. CONSTANT_Dynamic and indified String concatenation. I have no idea how these can be translated to Java 8...
  • please give me the link to docs that supports this statement "In javac terms, the -source parameter can't be greater than the -targetparameter." I didn't find anything like this here
  • Your correct that the docs don't mention that. The best resource I have been able to find says that, according to docs, the compiler should be able to do this. However, in practice it seems it won't. I'll add the best source I have been able to find on short notice.
  • Thank you for such a detailed answer. For me it seems easy just to transform high-level language (java) into low-level (bytecode). It appears to me that when the high-level language changes, the low-level should stay the same, that is why I thought it was possible. I am thinking about Kotlin now, it might be the way, what do you think?
  • 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. Then, since JDK 9, string concatenation gets compiled using invokedynamic referring to java.lang.invoke.StringConcatFactory which is not present in Java 8. Conversion of compiled classes is possible but not trivial.