Oracle 于四月宣布将万众瞩目的 Java 8 推迟到 2014 年第一季度发布(参见InfoQ 的报道)。
Oracle Java 平台小组的首席架构师 Mark Reinhold 在其博客中这样说道:
“在过去6 个月导致进度拖延的最主要工作是关于Project Lambda,这个该版本唯一重大特性的。
去年年底我们集成了语言和虚拟机为支持Lambda 所做的修改,但在处理所有相关变动和安全工作时,我们比预期花了更长的时间来完成stream API 的收尾以及相关核心库的增强。”
InfoQ 采访了 Oracle 的 Brian Goetz(JSR-335 规范的负责人),让他从内部视角发表了对 Project Lambda 的看法。
InfoQ**:JSR-335项目有很多项目需要协同,从并行集合到新的 Stream API。您能否在不涉及任何机密的情况下,以内部人员的视角透露一些内幕?你们是如何管理这些协同工作的?**
Brian:确实,这些改动部分之间的交互数目让人生畏,并且还有很多离散的部分:两个相关的专家组列表(一个为纯粹的语言特性,另一个包含库特性 JSR-166 专家组的成员)、OpenJDK 社区和 Oracle Java 平台组的多组件小组。但其回报也是十分丰厚的:与之前只能通过编译器语法糖来实现所有特性的平台演进工作不同,JSR-335 可以实现语言、库和虚拟机的协同进化,产生更好的结果。
在我看来,成功的关键是对目标保持清晰地关注。语言特性本身并不是一个目标。它们是推动者,肯定或否定某种风格和惯用法。即便在“添加 Lambda 表达式”这个范畴内,也常常会有影响方法的隐藏目标。 BGGA 提案的一个潜在目标是通过库来支持控制抽象。CICE 更多地关注更加适当的目标(减轻使用内部类的语法之殇)。而在 C#引入 Lambda 之时,更多的使用场景是用于 LINQ 。
对于 JSR-335 来说,我们清楚地认识到,语言特性是创建优秀库函数(如在现有集合上执行批量数据并行操作)的重要手段。语言特性造就了优秀的库函数,而优秀的库函数造就了简洁、清晰、不易出错的用户代码。因此我们收集了一系列想要实现的用户惯用语,如在集合上执行批量数据并行操作时,可以像下面这样:
复制代码
int sumOfWeights = anArrayList.parallelStream() .filter(b -> b.getColor() == BLUE) .mapToInt(b -> b.getWeight()) .sum();我们使用这些示例作为标准来确保我们在向着目标前进,而不仅仅是前进。
这个简单的示例暗示了:
- 需要在表达式中引入紧凑的编码行为(代码即数据)。
- 需要将迭代的控制从客户端(使用 for 循环这样的语言特性)转移到库函数中(内部迭代)。
- 需要在已有类型中添加新的方法,如 List.parallelStream(接口改变)。
- 需要具有更好的类型推断(使得编译器可以推断 Lambda 形参的类型)。
- 需要在“非线程安全”的数据结构上执行并行操作。
在实现一个大型语言特性时,所要面临的最严峻的挑战之一就是“特性蔓延”。在完成一个重大改变后,你总是希望继续引入其他的特性。(我们收到了来自社区的成百条建议,并且不得不对几乎所有建议说不。)只有对目标有清晰地认识,才能做出“这很酷,但超出了我们的范畴”的决定。
另一个关键的协同工具是在设计语言特性时使用正式的数学模型,如默认方法和类型推断行为的继承规则。自然语言特别不适合讨论这些复杂的问题,必然会导致误解。使用正式模型可以简单明了地讨论和理解一个特性的实际语义,并为规范、实现和测试提供蓝图。
InfoQ**:大多数 Project Lambda的活动似乎都属于 OpenJDK。如何提高社区参与度?**
Brian:在 Lambda-dev 邮件列表里有一批早期试用者,他们会定期下载最新构建(或从源代码构建)并尝试新的特性。这种体验驱动的反馈对于项目进程来说是至关重要的。早期试用者会在纠正成本尚低的时候找出 bug 和使用问题。社区能做的最有价值的帮助是:自己试写代码,并报告其体验(积极的或消极的)。
InfoQ****:Java诞生之后,除了 Invoke-Dynamic外,几乎所有的 JSR都涉及编译期变动。JSR 335有哪些字节码级别的变动呢?
Brian:Lambda 表达式严重依赖于 Java SE 7 中的 invokedynamic。有了 invokedynamic,编译器的开发者们可以避免很多虚拟机方面的工作。不过在我们添加的特性中,有两个是与虚拟机息息相关的,它们是默认方法(虚拟机用于继承,并最终影响 invokevirtual、invokeinterface 和 invokespecial 字节码的语义)和接口静态方法(大多会放宽现有类文件的限制)。
InfoQ**:貌似 IntelliJ Idea已经实现了一些 JSR-335兼容。是不是 IDE供应商们前段时间已经拿到了预览版本来进行开发?能否预测一下什么时候 NetBeans和 Eclipse会开始支持 JSR-335?**
Brian:我们确保所有的 IDE 供应商都派代表参加专家组,不但是为了确保这些特性能够被工具支持,同时 IDE 供应商还能较早地访问规范。NetBeans 很早以前就有了允许 Lambda 的构建版本,由于使用 javac 作为编译器,所以它们在这方面已经取得了领先。IntelliJ 已经在预览版和即将发布的 12.x 中取得了重大的 JSR-335 支持。当然,所有这一切都只能等规范发布后才能浮出水面。在那之前,什么都不确定。
InfoQ:Lambda 中一个令人激动的特性是,有了为管道和并发集合而设计的 Stream API,能否谈一下它们是如何设计、实现和协作的?
Brian:我们先考虑用户怎么使用方便,反过来再考虑设计。我们参考了其他语言的库,也包括 Java 的库如 LambdaJ。我们特别注意了它们的“展示”示例,并将其作为“需求”,来探索什么样的模型能支持它们。我们还发现以下三项是十分重要的,即在顺序和并发两种情况下都能使用流操作、现有集合可用作流的源,甚至包括非线程安全的集合用作并发流的源。我们为 API 设计制定了三个不同的迭代,每个迭代都基于上一个迭代。
InfoQ:常常会有人争论 Lambda和闭包的区别是什么。您的观点呢?
Brian:我认为考虑 Java SE 8 中的 Lambda 是不是“真正的”闭包是毫无意义的。对我来说,这里的“真正的”属于语法错误。很多语言都具有类闭包的构造,和闭包有着不同程度的相似和区别。认为 X 语言定义了真正的闭包,而其他语言如果没有完全相同的实现就不是真正的闭包是无益的。
也就是说,你很难决定是否支持捕获可变局部变量、非局部控制流、异常透明度和其他的特性,并且放弃其中一些特性意味着它们很难自然地表达。这是表现性和复杂度之间的权衡,我们希望将复杂性预算全部投入到对开发者最有影响的特性上。我们可以谈论这些决定的优缺点,但往“真假闭包”上扯还是算了吧。
InfoQ:闭包似乎给接口带来了一个悖论:我们平时所说的接口是指可能包含数据和结构但缺少实现的结构。而另一方面,闭包是作为数据的实际实现。Project Lambda是否允许我们通过定义常量闭包字段来在接口内实现一些功能呢?
Brian:代码即数据。(哥德尔 100 年前就这么教导我们。)Lambda 表达式只不过是让那些可简单地视为数据的行为,在语法上更加容易表示。
也就是说,接口缺乏实现的概念在 Java SE 8 中发生了改变。为了支持接口进化,我们允许接口提供可被类继承的方法的默认块,同时还允许在接口中定义静态方法。(接口包含默认方法可看做是某种形式的无状态 trait。)
InfoQ**:Lambda引入了一个“Optional”类型。它如何帮助我们避免空引用?**
Brian:我们对 Optional 的使用十分有限。它实际上只是用于方法的返回类型(该方法可能不返回任何东西)。比如 Map.get(key) 方法,如果 map 中不包含指定的键就会返回 null。这有两个缺点。首先,null 可能是某个 map 元素的有效值(某些 map 允许这样)。现在你无法区分“不存在”和“映射到 null”,而如果不得不再调用 containsKey() 来决定是哪种情况的话,你已经引入了一个竞态条件。其次,在代码中忘记检查 null 实在是太常见了,这使得代码缺乏可靠性。(并且 null 检查还会使代码变得丑陋。)当然,修改 Map.get() 为时已晚,但我们不会让错误再次发生。
Optional 可以描述一个非空值,或显式地设置为 empty。你不能对返回 Optional 的方法进行盲目地解引用,因为类型系统不允许。因此你只能显式地决定如果 optional 为 empty 时怎么处理。Optional 包含 get()(如果 Optional 为 empty 则抛出异常)、getOrElse(defaultValue)、getOrThrow(Supplier
等方法。因此你可以显式但却不冒失地指定如果值不存在时应该如何处理——抛出异常或返回默认值。 也就是说,熟悉 Scala 的 Option 的人可能要失望了。这并不是对类型系统多么深入的修改,它只是库中的一个辅助类,用来更加显式地表示“该方法可能不会返回任何东西”,而不是使用 null。
要了解最新的 Project Lambda 的情况,请访问 OpenJDK 网站上的 Project Lambda 页面。
关于受访者
Brian Goetz 是 Oracle 的 Java 语言架构师,也是 JSR-335(Lambda Expressions for the Java Language)规范的负责人。他是畅销书《 Java 并发编程实战》的作者,并且经常出席各大业内会议。
查看英文原文: Project Lambda from the Inside. An Interview with Brian Goetz
评论