Java 8 具有 Retrolambda 的子集

Retrolambda 允许你在 Java 7,6 或 5 上运行带有 lambda 表达式,方法引用和 try-with-resources 语句的 Java 8 代码。它通过转换 Java 8 编译的字节码来实现,以便它可以在较旧的 Java 运行时上运行。

移植语言功能:

  • Lambda 表达式通过将它们转换为匿名内部类来向后移植。这包括优化对无状态 lambda 表达式使用单例实例以避免重复的对象分配。方法引用基本上只是 lambda 表达式的语法糖,它们以相同的方式向后移植。

  • 如果目标字节码版本低于 Java 7,则通过删除对 Throwable.addSuppressed 的调用来反向尝试资源语句。如果你希望记录被抑制的异常而不是吞下,请创建一个功能请求,我们将使其可配置。

  • 如果目标字节码版本低于 Java 7,则 Objects.requireNonNull 调用将替换为对 Object.getClass 的调用。由 JDK 9 生成的合成空值检查使用 Objects.requireNonNull,而早期的 JDK 版本使用 Object.getClass

  • 也可选择:

    1. 通过将默认方法复制到伴随类(接口名称+“$”)作为静态方法,用抽象方法替换接口中的默认方法,以及通过向实现该接口的所有类添加必要的方法实现来反向移植默认方法。

    2. 通过将静态方法移动到伴随类(接口名称+“$”)并通过更改所有方法调用来调用新方法位置来对接口上的静态方法进行反向移植。

已知限制:

  • 不支持 Java 8 API

  • 在接口上向后移植默认方法和静态方法需要所有后向移植的接口和实现它们的所有类或调用它们的静态方法一起向后移植,一次执行 Retrolambda。换句话说,你必须始终进行干净的构建。此外,向后移植默认方法不适用于模块或依赖关系边界。

  • 如果未来的 JDK 8 构建停止为每个 invokedynamic 调用生成一个新类,可能会中断。Retrolambda 的工作方式是捕获 java.lang.invoke.LambdaMetafactory 动态生成的字节码,因此对该机制的优化可能会破坏 Retrolambda。

Retrolambda gradle 插件将使用 Retrolambda 自动构建你的 Android 项目。最新版本可以在发布页面上找到。

用法:

  1. 下载并安装 jdk8
  2. 将以下内容添加到你的 build.gradle
buildscript {
  repositories {
     mavenCentral()
  }

  dependencies {
     classpath 'me.tatarka:gradle-retrolambda:<latest version>'
  }
}

// Required because retrolambda is on maven central
repositories {
  mavenCentral()
}

apply plugin: 'com.android.application' //or apply plugin: 'java'
apply plugin: 'me.tatarka.retrolambda'

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

已知的问题:

  • Lint 在有 lambda 的 java 文件上失败。Android 的 lint 不懂 java 8 语法,会无声或大声失败。现在有一个实验分叉可以解决这个问题。

  • 使用 Google Play 服务会导致 Retrolambda 失败。版本 5.0.77 包含与 Retrolambda 不兼容的字节码。这应该在较新版本的播放服务中修复,如果你可以更新,那应该是首选的解决方案。要解决此问题,你可以使用早期版本(如 4.4.52)或将 -noverify 添加到 jvm args。

retrolambda {
  jvmArgs '-noverify'
}