JEP 126: Lambda 表达式与虚拟扩展方法
概述
向 Java 编程语言和平台添加 lambda 表达式(闭包)和支持功能,包括方法引用、增强的类型推断以及虚拟扩展方法。
目标
Lambda 表达式和虚拟扩展方法的主要特性,以及它们的一系列次要支持特性,进一步实现了多个平台目标:
- 简化更抽象、更高性能库的创建和使用
- 支持通过迁移兼容性实现更平滑的库演进
除了为 Java 编程语言添加现在常见的特性之外,lambda 表达式还通过启用内部迭代习惯用法,开启了改进的多核支持的可能性。
围绕 lambda 的支持语言特性包括 虚拟扩展方法,这将允许以源代码和二进制兼容的方式对接口进行演变。
除了语言的变更之外,协调库和 JVM 的变更也会发生。
请注意,正在进行的 Project Lambda OpenJDK 项目早于 JEP 流程,与其对应的 JSR,JSR 335,也同样是早于 JEP 流程,并且其目标是 Java SE 8 (JSR 336)。
非目标
函数类型和通用控制抽象的语言特性不是向 Java 添加 lambda 表达式的目标。然而,意图并不是要排除将来添加这些特性。
动机
许多其他常用的面向对象编程语言,包括那些托管在 JVM 上的语言(例如 Groovy、Ruby 和 Scala)以及托管在其他虚拟机上的语言(如 CLR 上的 C#),都包含对闭包的支持。因此,Java 程序员越来越熟悉这一语言特性及其所支持的编程模型。
特别令人感兴趣的是启用内部迭代的习惯用法。数组和集合目前支持外部迭代,其中迭代的控制逻辑位于被遍历的数据结构之外。例如,对数组或集合的 for
-each 循环就是外部迭代的一个例子。Java 中 for
循环的语义要求严格的串行迭代,这意味着程序员用来遍历标准集合的唯一方法将无法利用所有可用的核心。
通过内部迭代,数据结构被传入一段代码(以 Lambda 表达式的形式)来执行,而数据结构本身负责划分计算任务并报告结果。由于数据结构对其自身的内部细节非常熟悉,因此它可以通过调整一些选项(例如调度策略)来潜在地选择更优的计算调度方式。
- 交替执行顺序
- 使用线程池进行并发执行
- 使用分区和工作窃取进行并行执行。fork/join 框架 是 Java SE 7 中新增的一个候选并行执行框架,它在各种核心数量范围内提供了性能强劲的工作分区。
内部迭代风格的一个典型例子是一系列的 filter-map-reduce 操作,例如:
int maxFooWeight =
collection.filter( /* isFoo Predicate as a lambda */)
.map( /* Map a Foo to its weight with a lambda */)
.max(); /* Reduction step */
Lambda 表达式是一种具有简洁语法的表达式,用于表示所需的操作。这种风格代替了一个或多个显式的 for
循环,这些循环会不必要地限制对集合的迭代顺序。此外,一个设计良好的算法不仅可以并行执行这些操作集,还可以将这三个操作聚合为一次并行传递。
Project Lambda 还包括虚拟扩展方法,这将解决长期以来由于源代码兼容性问题而无法向广泛使用的接口添加方法的限制。
通过向现有的集合接口(如 java.util.Collection
和 java.util.List
)添加扩展方法,这些类型现有的实现可以参与到新的编程惯用法中。 JDK(以及其他地方)中这些类型的实现可以覆盖来自超接口的扩展方法的默认实现,从而提供更高性能或特定用途的实现。
描述
正在进行中的 OpenJDK Project Lambda 项目页面 和 对应的 JSR 拥有有关此工作细节的最新信息。
受整个 Project Lambda 影响的平台组件包括:
- Java 编程语言的规范
- Java 虚拟机的规范
javac
中语言变更的参考实现- 支持 lambda 表达式和虚拟扩展方法编译的类文件变更
- (可能的)JVM 增强功能以改善 lambda 的执行
- 支持虚拟扩展方法的 JVM 变更
- 核心 API 变更以添加虚拟扩展方法
- 核心 API 变更以支持 lambda 表达式的使用
- 更新 JDK 库以使用新的扩展方法
- 更新反射 API,例如核心反射和
javax.lang.model
,以暴露与 lambda 和扩展方法相关的信息 - 更新类文件工具如
javap
和pack200
/unpack200
以理解新的 JVM 属性 - (可能的)序列化更新,包括 IIOP 序列化
- 增强 javadoc 以指示哪些接口可以用于 lambda
java.lang.*
中的语言运行时以支持 lambda 表达式和虚拟扩展方法的转换
替代方案
Project Lambda 项目始于一个 初步提案,并经历了多次“Lambda 状态”迭代,这些迭代不仅改进了项目的语法,还扩展了其功能集。
Project Lambda 的 lambda 表达式部分的工作受到了许多先前努力的影响,包括但不限于:
Project Lambda 的功能集被认为比早期的提案更能满足 Java 平台发展的需求。
虚拟扩展方法 的设计同样受到编程语言社区大量前期工作的影响,包括:
- C++ 中的多重继承
- C# 中的静态 扩展方法
- Fortress 中的特征(traits)
- Scala 中的特征(traits)
- Strongtalk 中的混入(mixins)
与其他语言环境相比,Java 语言一直以来拥有更加可预测的语义:内置的基本类型具有已知的大小,异常会在精确的位置抛出,表达式的执行顺序被明确定义,等等。放宽内置 for
和 while
循环的语义以尝试实现自动并行化被认为既不可取,也不足以满足支持多核的目标。
测试
除了单元/回归测试之外,作为一项新语言特性,还将开发 JCK 测试。
该特性的实用性还将通过在 JDK 平台库中的使用来验证。
风险与假设
由于这项大型工作涉及平台的许多方面,可能会出现无法预见的复杂情况和相互作用。
通过提早开始并规划多个迭代,比如支撑库的工作,这些复杂情况的影响应该会得到缓解。
依赖
目前首选的 lambda 表达式实现方法依赖于 JSR 292 引入的 invokedynamic
和方法句柄。因此,lambda 的性能是否可接受取决于方法句柄的性能是否可接受,当然还有其他因素。
库的工作(JEPs 107 和 109)和语言特性之间预计会有反馈。
影响
- 其他 JDK 组件:其他 JDK 组件的影响在描述部分有所概述。
- 兼容性:虚拟扩展方法旨在允许对接口进行源代码兼容的演进。添加虚拟扩展方法
- 安全性:需要对 lambda 的运行时实现方面进行安全审查。
- 性能/可扩展性:需要跟踪对 lambda 友好的惯用法与传统惯用法的性能对比。
- 文档:需要编写用户指南和支持文档。
- TCK:作为一个大型平台特性,需要开发语言和库的 TCK(技术兼容性套件)。