多年前,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
分享到:
相关推荐
这里我们将详细讨论如何用C语言实现传递闭包、自反闭包和对称闭包这三种闭包算法。 首先,我们需要理解这些闭包的定义: 1. **传递闭包**:在一个关系集合中,如果对于所有的元素a、b和c,只要a与b有关系且b与c有...
接下来,文章着重讨论了原子闭包系统和原子闭包算子。对于有限原子格,如何描述它对应的闭包系统和闭包算子成为研究的关键。同时,文章还提出了原子全蕴含系统的概念,并研究了其与原子闭包算子之间的关系。 原子...
在这里,我们将详细讨论如何利用闭包来优化ZTree的性能,实现更高效的操作。 首先,让我们理解一下闭包的基本原理。闭包允许一个函数访问并操作外部函数的作用域中的变量,同时保持这些变量的状态。在JavaScript中...
本文将介绍一个在JavaScript经常会拿来讨论的话题 —— 闭包(closure)。 闭包其实已经是个老生常谈的话题了; 有大量文章都介绍过闭包的内容, 尽管如此,这里还是要试着从理论角度来讨论下闭包, 看看ECMAScript...
下面我们将详细讨论闭包的概念、工作原理以及如何在不同的编程语言中实现和应用闭包。 闭包的主要特征包括: 1. **记忆作用域**:闭包能够保持对创建它的作用域的引用,即使这个作用域在函数执行时已经不再存在。...
这种现象在社会学中被广泛讨论,因为它反映了人们倾向于与朋友的朋友建立关系的倾向,以增强社交网络的紧密性。 在数据验证过程中,我们通常会使用矩阵作为基础数据结构,特别是邻接矩阵。邻接矩阵是一个二维数组,...
- 关系幂运算与关系闭包讨论了关系的幂运算以及如何通过运算得到关系的闭包。 - 等价关系与序关系探讨了等价关系的分类和特性,以及序关系在序列化元素中的作用。 - 函数在集合之间建立了一种映射关系,是理解...
接下来,我们讨论偏序闭包。偏序关系比等价关系更为一般,它只需要满足自反性和反对称性,而不必满足传递性。在偏序集合中,如果每个元素都有最小上界(或最大下界),则该偏序可以升级为全序。偏序闭包是将原始偏序...
接下来,我们讨论闭包。闭包是JavaScript中一个强大的特性,它允许函数访问和操作其词法作用域内的变量,即使在其外部定义。闭包的关键在于函数能记住其被创建时的作用域,而不是执行时的作用域。 1. 闭包的应用...
此外,文章也讨论了完备剩余格的表示定理,这些定理提供了闭包算子在完备剩余格上的具体表达形式,以及它们的结构和性质。表示定理是研究完备剩余格上的闭包算子性质的基础工具,它们有助于我们理解闭包算子在完备...
此外,还有关于模糊关系闭包的讨论,闭包是指在某个特定性质下,关系可以扩展成为满足该性质的最大关系。而求解极小S-负传递闭包的算法需要根据余蕴涵的概念构建。 在文档末尾,还出现了一些关于模糊关系性质的描述...
最后,我们讨论一下`GCD(Grand Central Dispatch)`。GCD是Apple的多线程解决方案,它提供了一种有效管理并发任务的方式。在Swift3.0中,我们可以使用DispatchQueue来创建并控制不同类型的队列。主要分为串行队列和...
本示例重点讨论的是如何利用SuperMap Objects .NET 进行“根据距离分区域计算凸闭包多边形”的操作。这个功能在处理大量地理数据时尤其有用,例如在城市规划、环境分析、交通网络分析等场景。 凸闭包(Convex Hull...
在这个场景中,我们讨论的是一个名为"layer.rar"的压缩包,它包含了一个利用JavaScript闭包技术封装的提示模态框。这个模态框设计得既适应PC设备,也能在不同分辨率的设备上良好运行,体现了响应式设计的概念。 ...
在讨论JavaScript编程语言时,匿名函数和闭包是两个重要的概念,它们在函数式编程和模块化代码设计中扮演着核心角色。匿名函数是没有具体名称的函数,它们可以是独立的,也可以是表达式的一部分,通常用于定义临时...
#### 四、总结与讨论 本文提出的一种基于属性闭包的模式分解方法,能够有效地控制分解后的模式数量,简化数据库设计,并保证数据的一致性和完整性。这种方法特别适用于需要高效管理和操作的大规模数据库系统。此外...
7. **闭包的局限性**:讨论闭包可能导致的问题,如内存占用过多,以及如何有效管理闭包以避免性能问题。 8. **函数工厂与闭包**:介绍函数工厂模式,如何通过闭包创建可复用的函数。 9. **立即执行函数(IIFE)与...
接下来我们详细讨论一下百度地图API中的关键知识点: 1. **事件机制**:百度地图API提供了类似于DOM事件的事件机制,允许开发者监听和响应地图上的各种交互事件,如点击、拖动等。通过`addEventListener`和`...
不过,这里的讨论主要聚焦于基本的XMLHTTPRequest和JavaScript的Ajax实现。 总结,通过XMLHTTPRequest,我们可以创建自定义的Ajax请求,实现与服务器的异步通信。同时,利用JavaScript的闭包模式,我们可以有效地...