【51CTO精选译文】编者按:在上个月举行的Devoxx大会上,Sun宣布Java 7中将包含简单的闭包特性。这与之前一年Sun的宣布完全相反——当时他们说Java将不会考虑闭包,因为Java不需要。Java社区对此是有人欣喜有人怒,不过,到底有什么好争论的呢?一位对C++和Java十分了解,一直关注编程语言的资深开发者Marek Krj在最近的一篇博文中详细的分析了Java需要闭包的理由,并引申C++和C#的一些情况做为自己的论据。以下是Marek的讨论:
#t#多年前,Sun这样描述微软对Java的扩展提案:
Visual J++的许多优点——类型安全、面向对象、且易于组件互联(component interconnection)——只是对Java对象模型的安全性和灵活性的继承………
方法绑定(Bound)引用根本没有必要,它们并不属于Java编程语言的一个部分,因此它们也不能被标准的编译器接受。此外,这些做法会让Java语言损失掉简便性和统一性(unity)。
如此等等,这都是些营销时所说的胡言乱语。我不知道还有什么比这更恼人的:1.自诩面向对象的正统地位;2.自满:“我们更理解Java”。但无论如何,James Gosling(Java之父本人)也很可能会常说他希望闭包(closure)包含在Java中,而不是匿名内部类(anonymous inner classes);3. 对于如何将函数作为Java语言的头等公民(first class citiciens)那样包含进去,没有一个完整的提案!这就是他们所谓的“根本没有必要”的论据……
围绕着闭包的思想存在着相当大的争议,这有点像我们最近讨论过的关于C++ concepts特性的争论:人们认为它不必要,而且太复杂了(51CTO编者注:作者此处意为今年7月下旬,C++标准委员会确定将concepts特性从C++0x中移除一事。C++编程语言的作者Bjarne Stroustrup为此声称C++0x已死,将希望放在新一代C++标准上。)。由于在C++0x中,我们可以使用lambda函数,这没引起多少争议,因此Java社区对于闭包的这一争论激起了我的兴趣。来啊,为什么不引入闭包呢?甚至C#都包含了一些闭包!
那么,什么是闭包(Closure)呢?在这篇博文中,我们把它认为是类似于C++(或Python和Groovy)中lambda那样的函数,即一个匿名的、独立式的函数,比如下面这种:
- auto print = [] (int x) { cout << x; }
现在,让我们用Java语言来表示这一概念。
1.提案
那么,让我们来看一看这三个提案。
a) BGGA(源自Bracha、Gafter、Gosling 和 Ahe的首字母)
这是最雄心勃勃的一个提案。它引入了一个新的语言类型:函数类型,以及它的一个全新的标记方法(notation),这让我想起了的Haskell或lambda函数的演算类型(calculus types):
{T => U} 表示一个从T到U的函数{T => U => V} (或者这种形式{T,U => V}?) 表示一个从 T和U到V的函数
我认为,这种标记方法是许多Java从业者不太喜欢此提案的原因之一。另一个原因显然归因于非局部(nonlocal)环境语句(return ,break等)。新问题再次出现,非局部是指那个局部?嗯,举个例子来说吧,这意味着返回语句将不会从闭包返回,而是从调用该闭包的区域内返回。它们不是绑定在闭包上,而是绑定在一个环境中。为什么这会是一件好事呢?亲爱的读者,下一节将为您详细说明。
一个使用的例子:
- {Integer => void} print = { Integer x => System.out.println(x); }
b) CICE (简明实例创建方式,Concise Instance Creation Expressions)
这是三个提案中最简单的一个。基本上,它只起到在语法上不断定义匿名类的作用而已,没有其他的用处。没有第一类函数(first class functions)!没有晦涩难懂的函数编程理论!
以下是一个例子:
- public interface IPrint { public void invoke(Integer x);} // exacltly 1 method!
- Print print = IPrint(Integer x) { System.out.println(x); }
虽然CICE是最简单的提案,但我认为它也是最烦人的一个提案:你必须经常返回去查询一些接口的定义,真是太差劲了!
c) FCM(第一类方法,First Class Methods)
这是上面两个方案的折衷:它引入了第一类函数,但其闭包返回语句中并没有包含非局部绑定,而且去掉了让人讨厌的lambda形式符号。(51CTO编者注:FCM是此次Sun宣布通过的提案,详见Devoxx大会的相关报道。不过,作者这篇文章写于Devoxx之前,当时对此还并不知晓。)
以下是一个例子:
- #(void(Integer)) print = #(Integer x) { System.out.println(x); }
从以上三类的语法构成上来说,我最喜欢的BGGA提案,因为你能够用熟悉的代码块(code-block)方式来编程,而不必使用FCM的hash和CICE的接口标签。
- #(void(Integer)) print = #(Integer x) { System.out.println(x); }
2.争论
说得客气一点,我有时觉得这场辩论有点幼稚。下面是典型的辩点:
◆正方观点:如果没有闭包,我将永远不会用Java写哪怕一行代码,我会转而使用Scala进行编程。
◆反方观点:如果引入闭包,我将会崩溃掉……虽然我不会转向其它语言,但我会因此而怨恨你!
得了吧,各位!只不过是一个语言的特性而已。如果你不喜欢的话,你不使用它不就得了吗?而且,选择什么语言进行开发,是由项目经理决定,而不是由开发人员说了算!如果经理们认为一个语言能够带来某种好处,那么该语言就有可能被采用。但这与你是否喜欢Scala或其他所谓更好的语言没有半点的关系。
那么,闭包是否会使Java变得异常复杂?闭包是否真的如下面所说的那样:
“……使编程语言变得复杂,超过了正常的可用范围:主流程序员将放弃Java,人们还会继续争论下去,然后会转向使用其它更为简单的编程语言……
Java会变成一个只有小范围内的专家才会使用的稀有语言”?
那请你告诉我,对JVM而言什么语言是最简单的!我找不出最简单的语言(非脚本语言)。专家语言(Guru language)?你在开玩笑吗?闭包的目的是让Java变得更简单,因此每个人都应该开心才对。为什么要害怕呢?
一个可能的答案(非PC)是:认为“太复杂”的一方对Java的现状很满意,他们不关注语言优雅性的是否缺失,而且不想学习新的机制。或许这是因为在某种程度上,Java已经存在着太多的机制(或范型(paradigms))了?而对我来说,作为一个使用C++的开发人员,我已习惯了“多范型设计”,因为C++在这方面确实包罗万象。我不会去考虑那些我目前使用不到的机制。
也许,这对Java领域来说并不简单,因为多年以来Java程序员一直被灌输一种常识,即世界上只有面向对象这一种范型。这可以解释人们为什么认为泛型(generics)的加入会使语言的复杂性大大增加(另一个解释是设计选择上(特别对于通配符)需要考虑向后兼容性)。
因此,事情似乎是这样:要么是由于人们对泛型机制的愤怒,要么是由于BGGA提案中包含非局部变量返回这一不常用的做法,激起了人们的强烈反对。你难道不这样认为吗?
3.讨论
让我们的描述得更加技术一点:为什么我们在Java里面需要闭包呢?答案很可能是:因为Ruby语言里面有这个东西。Java 社区似乎还集体性的停留在一个过去Ruby所带来的创伤,而仿效Ruby的特性在其本身看来理由十分充分。正如过去人们在铁幕时期常常说的那样:学习Ruby就是学习如何取胜!所以现在,就像在Ruby(或者Groovy和C#)里一样,我们能够用Java这样编程:
- Utils.forEach(names, { Integer x => System.out.println(x); }); // BGGA
而这是我们在带有STL和lambda库的C++里面常常使用的东西。
不过,以上的叙述也不是争论的全部。上述三个提案中每个都有第二部分,能够允许包含某些细致的资源管理,可以认为这是一种“现代的析构函数”。我只能说“最终这样做了”,因为Java后期的资源管理非常麻烦。
BGGA试图通过上面提到的非本地控制语句来达到这个目标,然后可以允许执行“execute around”模式,即所谓的“控制抽象化”,但是有些不是很直观。在BGGA提案里,关键词是根据词汇绑定的,而且会自动指向定义了闭包的嵌套类型(enclosing type)实例。对我来说,Groovy一定程度上比较清楚的解决了非本地绑定的问题——引进了两个闭包成员:owner(关于嵌套对象)和delegate(定义词汇绑定)。通过这种方式,总是能够清楚的说明我们需要什么类型的词汇绑定,delegate的默认值是owner。
其他的提案也试图解决同样的问题:CICE包含了ARM(自动资源管理模块 —— 一个特殊的模块类型,当不使用它的时候会自动应用处理方法),而FCM 包含了JCA(Java控制抽象化 —— 这个很像上面提到的“execute around“那种类型,而且和BGGA一样需要非本地的词汇绑定)。
在这一点上,让我们试着得出的一些结论是:
第一:C#的3.0版本里面有lambda,C++0x里面同时包含有lambda和auto specifier,如果Java 7里再不包含闭包的话,就会显得十分陈旧了。
但也不要绝望,目前有一个库解决方案(http://code.google.com/p/lambdaj/)。在这种情况下,一个库解决方案是否足够呢?我不知道,因为它的语法看起来不是很直观,但是它可能已经是你能够得到的最佳方案了。
第二:我发现此处最有教育意义的内容是:我们能够看到析构函数的老概念其实一点也不过时!既然C#有它的使用声明,那么Java也理应(而且需要)包含一些类似于析构函数的机制。Java这一(前)“现代语言”也需要如此。
原文:Java's Closures Debate for C++ Eyes 作者:Marek Krj