JEP 126: Lambda Expressions & Virtual Extension Methods | Lambda 表达式和虚拟扩展方法
摘要
将 Lambda 表达式(闭包)和相关功能添加到 Java 编程语言和平台中,包括方法引用、增强类型推断和虚拟扩展方法。
目标
Lambda 表达式和虚拟扩展方法的主要特点以及它们的一系列辅助功能进一步实现了以下平台目标:
- 简化更抽象、更高性能库的创建和使用
- 支持更平滑的库演进与迁移兼容性
除了为 Java 编程语言添加一个现在常见的功能外,Lambda 表达式还通过启用内部迭代习惯为改进多核支持打开了可能性。
与 Lambda 相关的支持语言特性包括“虚拟扩展方法”,它允许接口以源代码和二进制兼容的方式演变。
除了语言的变化,还将进行协调的库和 JVM 的变化。
需要注意的是,“项目 Lambda”(Project Lambda)正在进行中的 OpenJDK 项目早于 JEP 过程,相应的 JSR JSR 335 也是如此,它的目标是 Java SE 8 (JSR 336)。
非目标
将 Lambda 表达式添加到 Java 中并不是为了实现函数类型和通用控制抽象。然而,意图并不排除将来可能添加这些特性。
动机
许多其他常用的面向对象编程语言,包括那些运行在 JVM 上的语言(例如 Groovy、Ruby 和 Scala)以及运行在其他虚拟机上的语言(C# 运行在 CLR 上),都支持闭包。因此,Java 程序员对于该语言特性和它所支持的编程模型越来越熟悉。
特别有趣的是,通过启用内部迭代习惯,可以实现更好的多核支持。目前,数组和集合只能支持“外部迭代”,即迭代的控制逻辑存在于被遍历的数据结构之外。例如,对数组或集合进行的 for each
循环就是外部迭代的一个例子。Java 中的 for
循环的语义要求严格的串行迭代,这意味着程序员在遍历标准集合时只能使用所有可用核心的一种方式。
通过内部迭代,数据结构会传递一个作为 Lambda 表达式的代码片段来执行,数据结构负责划分计算并报告结果。由于数据结构熟悉自己的内部细节,它可以通过调整以下选项来选择更好的计算调度:
- 替代执行顺序
- 使用线程池进行并发执行
- 使用分区和工作窃取进行并行执行。在 Java SE 7 中添加的 fork/join framework 就是一个候选的并行执行框架,它可以在各种核心数上提供性能强大的工作分区。
内部迭代风格的原型示例是一系列的过滤 - 映射 - 归约操作,例如:
int maxFooWeight =
collection.filter( /* isFoo Predicate as a lambda */)
.map( /* Map a Foo to its weight with a lambda */)
.max(); /* Reduction step */
Lambda 表达式是用简洁的语法表示所需操作的表达式。这种风格代替了一个或多个明确的 for
循环,这些循环将不必要地限制了集合的迭代顺序。此外,一个经过良好设计的算法不仅可以并行执行这些操作集,还可以将三个操作聚合为单一的并行遍历。
项目 Lambda 还包括虚拟扩展方法,它将解决长期存在的问题:由于源码兼容性的考虑,无法向广泛使用的接口添加方法。
通过向现有的集合接口(例如 java.util.Collection
和 java.util.List
)添加扩展方法,JDK 中和其他地方的这些类型的实现可以参与到新的编程习惯中。在提供更高性能或其他专用实现时,这些类型在 JDK(和其他地方)中的实现可以重写超接口的默认扩展方法实现。
描述
正在进行中的 Project Lambda OpenJDK 项目页面 和 相关 JSR 上有关于该工作的最新信息。
受整个 Project Lambda 工作影响的平台组件包括:
- Java 编程语言的规范
- 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 表达式和虚拟扩展方法的转换
替代方案
项目 Lambda 从一个 幼稚提议 开始,并经历了几次“Lambda 状态” 迭代,这些迭代改进了项目的语法和特性集。
Lambda 表达式部分的工作受到众多先前工作的影响,包括但不限于:
判断项目 Lambda 的特性集对于推进 Java 平台的演进需求更有效。
虚拟扩展方法的设计也受到了编程语言社区中大量先前工作的影响,包括:
- C++ 中的多重继承
- C# 中的静态扩展方法
- Fortress 中的特征
- Scala 中的特征
- Strongtalk 中的混入
与其他语言环境相比,Java 语言的语义一直更加可预测,内置的原始类型具有已知的大小,异常在精确的位置抛出,表达式的执行顺序被精确定义,等等。放松内置的 for
和 while
循环的语义以允许自动并行化尝试被认为既不理想也不足以满足支持多核的目标。
测试
除了单元/回归测试之外,作为一种新的语言特性,还将开发 JCK 测试。
通过在 JDK 平台库中使用该特性来验证其效用。
风险和假设
作为一个涉及平台多个方面的大型工作,可能会出现无法预料的复杂性和相互作用的可能性。
通过早期启动并计划进行多次迭代的支持库工作,应该能够减轻这些问题的影响。
依赖
目前首选的 Lambda 表达式实现方法依赖于 JSR 292 引入的 invokedynamic
和方法句柄。因此,Lambda 的可接受性性能取决于方法句柄的可接受性性能等因素。
支持库工作(JEP 107 和 109)与语言特性之间预计会有反馈。
影响
- 其他 JDK 组件:其他 JDK 组件的影响在描述部分已经概述。
- 兼容性:虚拟扩展方法旨在允许接口的源代码兼容演进。添加虚拟扩展方法
- 安全性:需要对 Lambda 的运行时实现的安全性进行审查。
- 性能/可伸缩性:应该跟踪使用 Lambda 友好风格与传统风格的性能差异。
- 文档:需要编写用户指南和支持文档。
- TCK:作为一个大型平台特性,需要开发语言和库的 TCK(技术兼容性套件)。