23

Lambdas were introduced in Java8. Will code containing lambda expressions run on an older JVM, say, for java 1.6? I am concerned about binary compatibility not source code compatibility. It is a simple Yes/No question.

Thanks.

user674669
  • 10,681
  • 15
  • 72
  • 105
  • 9
    No, it won't !! And it's easy to try :) – Pradeep Simha Apr 23 '17 at 07:30
  • 1
    Er ... your question is .... "will java 8 run on java 6?" – Tibrogargan Apr 23 '17 at 07:31
  • 10
    @Tibrogargan: It's not an unreasonable question, IMO. For example, lambda expressions were introduced in C# 3 along with .NET 3.5, but they didn't require any runtime changes, so they worked fine with a target of .NET 2. – Jon Skeet Apr 23 '17 at 07:33
  • 2
    See http://stackoverflow.com/questions/29143803 for some details which will explain why it won't work on Java 6. It suggests they should potentially be able to work with Java 7, but I haven't tried it. – Jon Skeet Apr 23 '17 at 07:34
  • 6
    @GhostCat I disagree. He/she specifically says "run on an older JVM" which implies that he/she means to take Java 8 bytecode with lambdas and run it on a Java 6 JVM. It's neither broad nor unclear - it's a simple yes/no question. Also, although it's easy to try, it's kind of a hassle to install a new JVM, just for the sake of finding a short answer to a simple question. **Spoiler** - the answer is no. – Dawood ibn Kareem Apr 23 '17 at 07:41
  • @Veera, do you know what error will be thrown? – user674669 Apr 23 '17 at 07:42
  • 2
    OK folks you convinced me. I put in an answer, retracted my vote and even upvoted the question :-) – GhostCat Apr 23 '17 at 08:57
  • 3
    See this related [answer](http://stackoverflow.com/questions/16143684/can-java-8-code-be-compiled-to-run-on-java-7-jvm/23757061#23757061) by Esko Luontola which gives some details which parts of Java 8 could *theoretically* be made to run on a *Java 7* VM. – Stefan Zobel Apr 23 '17 at 12:51
  • 2
    [Java is backward compatible, but why we need to upgrade many libraries when we upgrade jdk from 1.6 to 1.8?](http://stackoverflow.com/q/28228450/995714), [How will Java lambda functions be compiled?](http://stackoverflow.com/q/16827262/995714), [A program made with Java 8 can be run on Java 7?](http://stackoverflow.com/q/22610400/995714) – phuclv Apr 23 '17 at 14:59

3 Answers3

39

Oracle tries very hard to keep the Java language and the JVM bytecode language separate. The Java Language Specification only says what a program that contains a lambda expression means, it doesn't say anything about how this program is supposed to be compiled or interpreted.

This means specifically that there is nothing in the Java Language Specification that forbids that lambda expressions are compiled in such a way that the resulting code can be executed by a Java 6 JVM, but there is nothing in the Java Language Specification that guarantees it either. Every Java vendor is allowed to encode lambda expressions in whatever way they want. (Obviously, for pragmatic reasons, most try to match whatever Oracle does pretty closely. That way, for example, debuggers / de-compilers / tools which can understand and reverse-engineer lambdas encoded by Oracle's javac will automatically work with bytecode produced by IBM J9 JDK's Java compiler.)

The javac compiler which ships with the Oracle JDK encodes lambda expressions using a rather complicated machinery of LambdaMetafactory, MethodHandles, and invokedynamic. The latter was only introduced in the Java 7 JVM, so this means that the specific encoding that Oracles JDK's javac uses requires at least a Java 7 JVM. But other encodings are definitely possible, none of this complex machinery is truly necessary, it is just a performance optimization. You could e.g. encode Lambda Expressions as inner classes, which would work down to the Java 1.1 JVM – this is after all exactly how we have written "poor man's lambdas" up to Java 8; it's also how original proposals for lambdas and even early previews of Java 8 implemented it, after all, the development of lambdas in Java pre-dates even Java 7 and invokedynamic.

There is a compiler called RetroLambda, which compiles Java 8 JVM bytecode (not Java source code!) produced by Oracle JDK javac to Java 7 JVM bytecode, Java 6 JVM bytecode, or Java 5 JVM bytecode. Using this compiler, you can produce a class file containing bytecode that will run on any Java 5 or newer JVM from Java 8 source code that uses (almost) all features of Java 8.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 3
    Definitely the more helpful answer. I updated mine to be more accurate, and I will do my part to get yours the reputation it deserves. – GhostCat Apr 23 '17 at 16:51
  • 1
    …and you could even transform the inner classes to an 1.0 compatible form. – Holger Apr 24 '17 at 10:40
16

Short answer: it depends.

If you are completely restricted to Oracle javac and libraries, the answer is: no; for the following reasons.

Java bytecode contains a major version number. By default, a Java 8 compiler puts java8 into the data. Any older JVM simply refuses to run that code. It is possible though to tell a Java 8 compiler to create bytecode compatible to older JVMs. But: in order for the older JVM to execute such "special" class files, you need all its dependencies to be available!

And there it breaks: Lambdas make use of the invokedynamic bytecode instruction which doesn't exist in Java 6. And beyond that, the compiler makes use of a large number of Java library stuff when compiling lambdas - all of them added after java 6.

So even when you would manage to compile lambda using source code to Java 6 bytecode - that instruction isn't available,and you need to provide all those other classes, too.

But: as the other great answer explains, there are alternatives to javac which allow you to use lambdas on older JVMs.

But: be careful how to spend your energy. Java 6 is still dead for "server side java". So using these alternatives is OK for platforms like Android, but when you are still running an Oracle Java 6 JVM somewhere, you should rather spend your energy on upgrading that system to a current version of Java.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 6
    There is nothing in the Java Language Specification that says that Lambdas must use `invokedynamic`. That's just the way that one particular version of one particular compiler by one particular company happens to encode them at the moment. And in fact, there *are* implementations of Java8 lambdas that don't use `invokedynamic`. – Jörg W Mittag Apr 23 '17 at 13:52
  • 1
    Can you give a link with more information? I will see how to update my answer accordingly. But having said so, the core point is: porting arbitrary lambdas would require considerable effort. – GhostCat Apr 23 '17 at 14:03
  • 12
    RetroLambda compiles Java 8 JVM bytecode to Java 5/6/7 JVM bytecode. It encodes Lambda Expressions like we have always done it: as inner classes. The whole Lambda Factory / Method Handle / `invokedynamic` dance is a performance optimization, nothing more. It is in no way required for the semantics of lambdas; after all, we could write "poor man's lambdas" ever since Java 1.1 using SAM interfaces and inner classes. – Jörg W Mittag Apr 23 '17 at 14:12
  • 5
    @GhostCat "Porting arbitrary lambdas" can be done by rewriting the *invokedynamic recipe* in the Java 8 class files by compile time generated anonymous classes. There are bytecode retrotranslator [tools](https://github.com/orfjackal/retrolambda) doing this. The very early JDK 8 builds also generated anonymous classes at compile time and the Android *Jack* / *desugar* toolchains do the very same thing. – Stefan Zobel Apr 23 '17 at 14:17
  • Never say never. I hear you folks, and will factor that into my answer as soon as I get my hands on a real keyboard. Which might take a while. Of course, the next question then is: to which degree does that matter in practical reality? – GhostCat Apr 23 '17 at 14:23
  • 4
    [retrolambda](https://github.com/orfjackal/retrolambda) has been very popular (and still is) on the Android platform as there are lot of practical problems with the new Android Studio Jack toolchain that should make it redundant in theory. It even has its own SO [tag](http://stackoverflow.com/questions/tagged/retrolambda). There are also some semi-popular Java 8 backport libraries that use it in practice. – Sartorius Apr 23 '17 at 14:33
  • 2
    Fair point about Java 6 being "dead". I wish that were true. In fact, I know an organization from the banking sector which is still on Sun JVM 6 (these are the same guys that still use arcane technologies like [IDMS](https://en.wikipedia.org/wiki/IDMS) and languages like COBOL). The funny thing is the Java developers there aren't afraid to use retrolambda in production. Same thing for a manufacturer from the automotive sector I've worked for. I'm afraid there are always exceptions :( – Sartorius Apr 23 '17 at 17:24
  • 2
    I hope those bank(st)ers keep those systems really isolated from everything. Or maybe they have their own team that creates security fixes for their JVM (but probably not so). And, yes, never say never. I learned that, too today. – GhostCat Apr 23 '17 at 17:28
9

As others said: no

The class files produced by the javac compiler are versioned. Everything that is produced by Java 8 is flagged to require a Java 8 JVM by default. Older versions of the JVM won't accept the binary.

If you want to compile for an older platform, you have to tell javac: javac -target 6. As soon as you add this option to the command line, your Java 8 compiler will require that you specify -source 6 (or lower) as well. Therefore it will not accept any code that contains lambda expressions.

Matthias Wimmer
  • 3,789
  • 2
  • 22
  • 41