大辩论:Java不是C++ 闭包让Java更简单

原创
开发 后端
Java真的需要闭包吗?或者说,Java真的不需要闭包吗?Java闭包的三个提案中,FCM提案最终得到了通过。这增加的一个语言特性,真的会令Java变得异常复杂么?本文作者认为,人们不应太担心这个,而是应该为了闭包使Java更简单而感到高兴。

【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那样的函数,即一个匿名的、独立式的函数,比如下面这种:

  1. 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等)。新问题再次出现,非局部是指那个局部?嗯,举个例子来说吧,这意味着返回语句将不会从闭包返回,而是从调用该闭包的区域内返回。它们不是绑定在闭包上,而是绑定在一个环境中。为什么这会是一件好事呢?亲爱的读者,下一节将为您详细说明。

一个使用的例子:

  1. {Integer => void} print = { Integer x => System.out.println(x); } 

b) CICE (简明实例创建方式,Concise Instance Creation Expressions)

这是三个提案中最简单的一个。基本上,它只起到在语法上不断定义匿名类的作用而已,没有其他的用处。没有第一类函数(first class functions)!没有晦涩难懂的函数编程理论!

以下是一个例子:

  1. public interface IPrint { public void invoke(Integer x);} // exacltly 1 method!  
  2. Print print = IPrint(Integer x) { System.out.println(x); } 

虽然CICE是最简单的提案,但我认为它也是最烦人的一个提案:你必须经常返回去查询一些接口的定义,真是太差劲了!

c) FCM(第一类方法,First Class Methods)

这是上面两个方案的折衷:它引入了第一类函数,但其闭包返回语句中并没有包含非局部绑定,而且去掉了让人讨厌的lambda形式符号。(51CTO编者注:FCM是此次Sun宣布通过的提案,详见Devoxx大会的相关报道。不过,作者这篇文章写于Devoxx之前,当时对此还并不知晓。)

以下是一个例子:

  1. #(void(Integer)) print = #(Integer x) { System.out.println(x); } 

从以上三类的语法构成上来说,我最喜欢的BGGA提案,因为你能够用熟悉的代码块(code-block)方式来编程,而不必使用FCM的hash和CICE的接口标签。

  1. #(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这样编程:

  1. 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

责任编辑:yangsai 来源: 51CTO.com
相关推荐

2015-10-15 11:27:02

cookie安全总结

2015-05-26 15:17:44

OpenStack

2013-06-09 16:49:56

布线系统TIA40Gb

2011-09-27 09:16:36

JavaScript

2022-05-06 16:18:00

Block和 C++OC 类lambda

2011-07-27 16:55:12

Objective-c 闭包

2012-04-17 09:17:35

HTML 5App应用

2015-05-20 12:50:42

C#开发抽象增删改

2021-03-15 10:29:50

人工智能

2011-05-16 17:02:44

云计算

2012-06-20 13:36:42

Surface平板

2009-06-15 09:43:11

Java闭包

2015-10-21 16:47:47

Testin云测

2010-05-13 15:41:19

Java 7JDK 7闭包

2024-01-22 09:51:32

Swift闭包表达式尾随闭包

2019-07-10 10:20:36

前端用户体验javascript

2019-04-04 14:05:20

consolejs前端

2011-08-05 09:33:30

Func局部变量作用域

2010-06-01 09:21:52

Java 7闭包

2017-07-17 14:17:37

闭包匿名函数 作用域
点赞
收藏

51CTO技术栈公众号