微信号:infoqchina

介绍:有内容的技术社区媒体

专访Brian Goetz:Project Lambda探秘

2013-08-30 16:41 InfoQ

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来说,我们清楚地认识到,语言特性是创建优秀库函数(如在现有集合上执行批量数据并行操作)的重要手段。语言特性造就了优秀的库函数,而优秀的库函数造就了简洁、清晰、不易出错的用户代码。因此我们收集了一系列想要实现的用户惯用语,如在集合上执行批量数据并行操作时,可以像下面这样:



我们使用这些示例作为标准来确保我们在向着目标前进,而不仅仅是前进。


这个简单的示例暗示了:

  • 需要在表达式中引入紧凑的编码行为(代码即数据)。

  • 需要将迭代的控制从客户端(使用for循环这样的语言特性)转移到库函数中(内部迭代)。

  • 需要在已有类型中添加新的方法,如List.parallelStream(接口改变)。

  • 需要具有更好的类型推断(使得编译器可以推断Lambda形参的类型)。

  • 需要在“非线程安全”的数据结构上执行并行操作。

在实现一个大型语言特性时,所要面临的最严峻的挑战之一就是“特性蔓延”。在完成一个重大改变后,你总是希望继续引入其他的特性。(我们收到了来自社区的成百条建议,并且不得不对几乎所有建议说不。)只有对目标有清晰地认识,才能做出“这很酷,但超出了我们的范畴”的决定。


另一个关键的协同工具是在设计语言特性时使用正式的数学模型,如默认方法和类型推断行为的继承规则。自然语言特别不适合讨论这些复杂的问题,必然会导致误解。使用正式模型可以简单明了地讨论和理解一个特性的实际语义,并为规范、实现和测试提供蓝图。


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<E extends Throwable>等方法。因此你可以显式但却不冒失地指定如果值不存在时应该如何处理——抛出异常或返回默认值。


也就是说,熟悉Scala的Option的人可能要失望了。这并不是对类型系统多么深入的修改,它只是库中的一个辅助类,用来更加显式地表示“该方法可能不会返回任何东西”,而不是使用null。


嘉宾简介:


Brian Goetz是Oracle的Java语言架构师,也是JSR-335(Lambda Expressions for the Java Language)规范的负责人。他是畅销书《Java并发编程实战》的作者,并且经常出席各大业内会议。


注:原文较长,这里为部分节选内容,请点击全文链接查看原文。


***********************************

本文来自InfoQ微信公众账号:infoqchina

1、回复“今日新闻”,查看今天更新的新闻;

2、回复“今日英文”,查看今天英文站的更新;

3、回复“文章 +关键词”,搜索关键词相关内容;

4、回复“QCon”,了解QCon大会相关信息;

5、回复“活动”,了解最近InfoQ组织的线下沙龙;

6、回复“架构师”,获取《架构师》下载地址;

7、回复“投稿”,了解投稿流程。

***********************************

 
InfoQ 更多文章 Facebook如何实现PB级别数据库自动化备份 学术派Google软件工程师Matt Welsh谈移动开发趋势 Spotify为什么要使用一些“无聊”的技术? 妹纸们放假了,汉纸们做啥? 大多数重构可以避免
猜您喜欢 第四届持续交付大会 - 深圳站 你知道了吗?2015年网页设计的9大趋势 一个故事告诉你和运营大牛的区别——运营小白上班记 2014-2019全球云指数报告摘要 从0到1建设一个电商网站,技术选型和注意事项有哪些?