Home

Awesome

Retrolambda: Use Lambdas on Java 7

Retrolambda presentation video

Just as there was Retroweaver et al. for running Java 5 code with generics on Java 1.4, Retrolambda lets you run Java 8 code with lambda expressions, method references and try-with-resources statements on Java 7, 6 or 5. It does this by transforming your Java 8 compiled bytecode so that it can run on an older Java runtime. After the transformation they are just a bunch of normal .class files, without any additional runtime dependencies. Read more details.

There is also limited support for backporting default methods and static methods on interfaces. This feature is disabled by default.

Retrolambda supports backporting to Java 7, Java 6 and Java 5 runtimes. And for adventurous developers there are other backporting tools that may let you go from Java 5 down to Java 1.4.

Nowadays Android Studio has built-in support for Java 8 features, so that is probably the first thing to try out. Otherwise, Retrolambda works also for Android: Serge Zaitsev has written an article about it and there is a Gradle plugin which makes it easy.

Retrolambda does not backport the new Java 8 APIs, but there are other projects that have backported some of them:

Additionally Animal Sniffer and IntelliJ IDEA can warn about the use of Java 8 APIs.

User Guide

Retrolambda can be run as a Maven plugin, Gradle plugin or command line application. Also have a look at some tips for using Retrolambda effectively.

Maven Plugin

To run Retrolambda using Maven, add the following to your pom.xml:

<plugin>
    <groupId>net.orfjackal.retrolambda</groupId>
    <artifactId>retrolambda-maven-plugin</artifactId>
    <version>2.5.7</version>
    <executions>
        <execution>
            <goals>
                <goal>process-main</goal>
                <goal>process-test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

See the plugin documentation for all possible parameters. There is also a usage example in end-to-end-tests/pom.xml

Gradle Plugin

Gradle Retrolamba Plugin is developed by Evan Tatarka. See its site for usage instructions.

Command Line Application

Download the latest retrolambda.jar from Maven Central.

Use JDK 8 to compile your source code.

Run Retrolambda, using Java 8, on the class files produced by JDK 8. Run java -jar retrolambda.jar without any additional options to see the instructions (for your convenience they are also shown below).

Your class files should now run on Java 7 or older.

Usage: java -Dretrolambda.inputDir=? -Dretrolambda.classpath=? [-javaagent:retrolambda.jar] -jar retrolambda.jar

Retrolambda takes Java 8 classes and backports lambda expressions and
some other language features to work on Java 7, 6 or 5.
Web site: https://github.com/luontola/retrolambda

Copyright (c) 2013-2017  Esko Luontola and other Retrolambda contributors
This software is released under the Apache License 2.0.
The license text is at http://www.apache.org/licenses/LICENSE-2.0

Configurable system properties:

  retrolambda.bytecodeVersion
      Major version number for the generated bytecode. For a list, see
      offset 7 at http://en.wikipedia.org/wiki/Java_class_file#General_layout
      Default value is 51 (i.e. Java 7)

  retrolambda.defaultMethods
      Whether to backport default methods and static methods on interfaces.
      LIMITATIONS: All backported interfaces and all classes which implement
      them or call their static methods must be backported together,
      with one execution of Retrolambda.
      Disabled by default. Enable by setting to "true"

  retrolambda.inputDir (required)
      Input directory from where the original class files are read.

  retrolambda.outputDir
      Output directory into where the generated class files are written.
      Defaults to same as retrolambda.inputDir

  retrolambda.classpath (required)
      Classpath containing the original class files and their dependencies.
      Uses ; or : as the path separator, see java.io.File#pathSeparatorChar

  retrolambda.classpathFile (alternative)
      File listing the classpath entries.
      Alternative to retrolambda.classpath for avoiding the command line
      length limit. The file must list one file per line with UTF-8 encoding.

  retrolambda.includedFiles
      List of files to process, instead of processing all files.
      This is useful for a build tool to support incremental compilation.
      Uses ; or : as the path separator, see java.io.File#pathSeparatorChar

  retrolambda.includedFilesFile (alternative)
      File listing the files to process, instead of processing all files.
      Alternative to retrolambda.includedFiles for avoiding the command line
      length limit. The file must list one file per line with UTF-8 encoding.

  retrolambda.javacHacks
      Attempts to fix javac bugs (type-annotation emission for local variables).
      Disabled by default. Enable by setting to "true"

  retrolambda.quiet
      Reduces the amount of logging.
      Disabled by default. Enable by setting to "true"

If the Java agent is used, then Retrolambda will use it to capture the
lambda classes generated by Java. Otherwise Retrolambda will hook into
Java's internal lambda dumping API, which is more susceptible to suddenly
stopping to work between Java releases.

Tips

Be sure to run comprehensive tests on your target JVM version (e.g. Java 7), in case the code accidentally uses Java 8 APIs or language features that Retrolambda doesn't backport.

During development, inside an IDE, it's the easiest to use Java 8, without Retrolamba, to compile and run tests. But in your continuous integration and release builds you should run all tests using the target Java version. For example, you can configure Maven Surefire Plugin to run tests using a different JVM.

I recommend setting up environment variables JAVA8_HOME, JAVA7_HOME etc. and referring to those variables in the build configuration, instead of relying on what happens to be the default Java version in JAVA_HOME.

You will need Java 8 for compiling and also for generating Javadocs. JDK 7's Javadoc tool will fail for some valid Java 8 code.

Backported Language Features

Lambda expressions are backported by converting them to anonymous inner classes. This includes the optimization of using a singleton instance for stateless lambda expressions to avoid repeated object allocation.

Method references are basically just syntax sugar for lambda expressions and they are backported in the same way.

Try-with-resources statements are backported by removing calls to Throwable.addSuppressed if the target bytecode version is below Java 7. If you would like the suppressed exceptions to be logged instead of swallowed, please create a feature request and we'll make it configurable.

Objects.requireNonNull calls are replaced with calls to Object.getClass if the target bytecode version is below Java 7. The synthetic null checks generated by JDK 9 use Objects.requireNonNull, whereas earlier JDK versions used Object.getClass.

Optionally also:

Default methods are backported by copying the default methods to a companion class (interface name + "$") as static methods, replacing the default methods in the interface with abstract methods, and by adding the necessary method implementations to all classes which implement that interface.

Static methods on interfaces are backported by moving the static methods to a companion class (interface name + "$"), and by changing all methods calls to call the new method location.<sup>[1]</sup>

<sup>[1]</sup> The static methods are moved to a companion class even with default method support disabled, because some of them may be lambda implementation methods, but the method calls to static methods are not updated. This may cause weird error messages if static methods on interfaces are accidentally used without enabling default method support.

Known Limitations

Does not backport Java 8 APIs.

Backporting default methods and static methods on interfaces requires all backported interfaces and all classes which implement them or call their static methods to be backported together, with one execution of Retrolambda. In other words, you must always do a clean build. Also, backporting default methods won't work across module or dependency boundaries.

May break if a future JDK 8 build stops generating a new class for each invokedynamic call. Retrolambda works so that it captures the bytecode that java.lang.invoke.LambdaMetafactory generates dynamically, so optimizations to that mechanism may break Retrolambda.

Java 9 and higher are not supported; just build your project with Java 8. The new JDKs mostly just add new APIs, which you anyways wouldn't be able to use on on older JREs. To backport new language features, create a new tool for it yourself or pay someone to do it, if you think it's worth the effort. ;)

Version History

Retrolambda 2.5.7 (2020-01-23)

Retrolambda 2.5.6 (2018-11-30)

Retrolambda 2.5.5 (2018-08-14)

Retrolambda 2.5.4 (2018-05-30)

Retrolambda 2.5.3 (2017-12-28)

Retrolambda 2.5.2 (2017-12-28)

Retrolambda 2.5.1 (2017-02-23)

Retrolambda 2.5.0 (2017-01-22)

Retrolambda 2.4.0 (2017-01-11)

Retrolambda 2.3.0 (2016-04-30)

Retrolambda 2.2.0 (2016-04-29)

Retrolambda 2.1.0 (2015-12-19)

Retrolambda 2.0.6 (2015-09-06)

Retrolambda 2.0.5 (2015-07-19)

Retrolambda 2.0.4 (2015-07-08)

Retrolambda 2.0.3 (2015-06-07)

Retrolambda 2.0.2 (2015-04-14)

Retrolambda 2.0.1 (2015-04-06)

Retrolambda 2.0.0 (2015-03-28)

Retrolambda 1.8.1 (2015-01-06)

Retrolambda 1.8.0 (2014-11-16)

Retrolambda 1.7.0 (2014-10-21)

Retrolambda 1.6.2 (2014-10-03)

Retrolambda 1.6.1 (2014-08-25)

Retrolambda 1.6.0 (2014-08-20)

Retrolambda 1.5.0 (2014-07-19)

Retrolambda 1.4.0 (2014-07-04)

Retrolambda 1.3.0 (2014-06-04)

Retrolambda 1.2.3 (2014-05-19)

Retrolambda 1.2.2 (2014-05-15)

Retrolambda 1.2.1 (2014-05-04)

Retrolambda 1.2.0 (2014-05-02)

Retrolambda 1.1.4 (2014-03-29)

Retrolambda 1.1.3 (2014-03-25)

Retrolambda 1.1.2 (2014-01-08)

Retrolambda 1.1.1 (2013-11-27)

Retrolambda 1.1.0 (2013-07-25)

Retrolambda 1.0.0 (2013-07-23)