Java是否需要closures?

最近我读了很多关于Java的下一个版本,可能会支持闭包 。 我觉得我对于闭包有一个非常牢固的把握,但我想不出一个如何将面向对象的语言“更好”的坚实范例。 任何人都可以给我一个特定的用例,需要closures(甚至是首选)?

作为一名Lisp程序员,我希望Java社区理解以下差异: 函数作为对象与闭包

a) function可以被命名或匿名 。 但是他们也可以是自己的对象。 这允许函数作为parameter passing,从函数返回或存储在数据结构中。 这意味着函数是编程语言中的第一类对象。

匿名函数不会增加很多的语言,它们只是让你用更短的方式编写函数。

b) 闭包是一个function加上一个绑定环境 。 闭包可以向下传递(作为参数)或向上返回(作为返回值)。 这允许函数引用其环境的variables,即使周围的代码不再有效。

如果你有某种语言,那么问题就出现了。 有语言有a) ,但不是b) 。 在函数式编程世界中, a) (函数)和b (函数作为闭包)现在是常态。 Smalltalk有a)是匿名函数)很长一段时间,但是一些Smalltalk的方言增加了对b)的支持(块作为闭包)。

你可以想象,如果你添加函数和闭包语言,你会得到一个稍微不同的编程模型。

从实用的angular度来看,匿名函数会在传递或调用函数的地方添加一些简短的符号。 这可能是一件好事。

闭包(函数加绑定)允许你创build一个可以访问某些variables的函数(例如一个计数器值)。 现在,您可以将该函数存储在一个对象中,访问它并调用它。 函数对象的上下文现在不仅是它可以访问的对象,还包括它通过绑定访问的variables。 这也是有用的,但是你可以看到variables绑定与对象variables的访问是一个问题:什么时候应该是一个词汇variables(可以在闭包中访问),什么时候应该是某个对象的variables一个插槽 )。 什么时候应该是封闭或对象? 你可以用类似的方法使用两者。 学生学习Scheme(一种Lisp方言)的常用编程练习是使用闭包编写一个简单的对象系统。

结果是一个更复杂的编程语言和更复杂的运行时模型。 太复杂?

他们不会使面向对象的语言更好。 他们使实用语言更实用。

如果你用OO锤攻击一个问题 – 把所有东西都当作对象之间的交互 ​​- 那么封闭就没有意义了。 在一个以class级为基础的面向对象的语言中,closures是烟雾缭绕的后台,在这个后台,任何事情都可以完成,但事后没有人谈论。 从概念上讲,这是可恶的。

在实践中,这是非常方便的。 我真的不想定义一个新types的对象来保存上下文,为它build立“do stuff”方法,实例化它,并填充上下文…我只想告诉编译器:“看,看看我现在可以访问吗?这是我想要的上下文,这里是我想要使用它的代码 – 为我保留“直到我需要它”。

神奇的东西。

最明显的将是所有那些只有一个方法run()或actionPerformed()或类似的类的伪replace。 因此,不是使用embedded的Runnable创build线程,而是使用闭包。 没有比现在更强大,但更方便简洁。

那么我们需要closures吗? 他们会很高兴有吗? 当然,只要他们不感到狂躁,就像我害怕他们会那样。

我想支持核心function编程概念,你需要closures。 使代码更加优雅,并支持闭包 。 另外,我喜欢将代码行作为parameter passing给函数。

有一些非常有用的“高阶函数”可以使用闭包对列表进行操作。 高阶函数是以“函数对象”为参数的函数。

例如,对列表中的每个元素应用一些转换是非常常见的操作。 这个高阶函数通常被称为“地图”或“收集”。 (请参阅Groovy的*。spread操作符)。

例如,要将列表中的每个元素放在一个列表中而不用closures,你可能会写:

List<Integer> squareInts(List<Integer> is){ List<Integer> result = new ArrayList<Integer>(is.size()); for (Integer i:is) result.add(i*i); return result; } 

使用闭包和映射以及build议的语法,你可以这样写:

 is.map({Integer i => i*i}) 

(这里有一个可能的性能问题,关于装箱原始types。)

正如Pop Catalin所解释的那样,另一个更高阶的函数叫做“select”或“filter”:它可以用来获得符合某种条件的列表中的所有元素。 例如:

代替:

 void onlyStringsWithMoreThan4Chars(List<String> strings){ List<String> result = new ArrayList<String>(str.size()); // should be enough for (String str:strings) if (str.length() > 4) result.add(str); return result; } 

相反,你可以写一些像

 strings.select({String str => str.length() > 4}); 

使用该build议。

您可以看看Groovy语法,它是Java语言的扩展,现在支持闭包。 有关更多示例如何处理闭包,请参阅Groovy用户指南集合一章。

一句话:

关于“closures”一词可能需要一些澄清。 我上面显示的是严格的说,没有closures。 他们只是“function对象”。 闭包就是一切可以捕获或者“closures”它周围代码的(词汇)上下文的东西。 从这个意义上说,现在在Java中是封闭的,即匿名类:

 Runnable createStringPrintingRunnable(final String str){ return new Runnable(){ public void run(){ System.out.println(str); // this accesses a variable from an outer scope } }; } 

Java不需要闭包,一个面向对象的语言可以完成一个闭包使用中间对象来存储状态或做动作(在Java的情况下,内部类)。 但是闭包作为一个特性是很有必要的,因为它们大大简化了代码,提高了可读性,从而提高了代码的可维护性。

我不是Java专家,但是我正在使用C#3.5,并且闭包是我最喜欢的语言特性之一,例如,以下面的语句为例:

 // Example #1 with closures public IList<Customer> GetFilteredCustomerList(string filter) { //Here a closure is created around the filter parameter return Customers.Where( c => c.Name.Contains(filter)).ToList(); } 

现在拿一个不使用闭包的等价例子

 //Example #2 without closures, using just basic OO techniques public IList<Customer> GetFilteredCustomerList(string filter) { return new Customers.Where( new CustomerNameFiltrator(filter)); } ... public class CustomerNameFiltrator : IFilter<Customer> { private string _filter; public CustomerNameFiltrator(string filter) { _filter = filter; } public bool Filter(Customer customer) { return customer.Name.Contains( _filter); } } 

我知道这是C#,而不是Java,但想法是一样的,闭包有助于简洁,使代码更短,更具可读性。 在幕后,C#3.5的闭包做了一些与示例#2非常相似的事情,这意味着编译器在后台创build一个私有类并将“filter”parameter passing给它。

Java不需要closures工作,作为一个开发者,你也不需要它们,但是它们是有用的,并且提供了好处,所以这意味着它们是一种生产语言所需要的语言,它的目标之一就是生产力。

最近我读了很多关于Java的下一个版本,可能会支持闭包。 我觉得我有一个非常牢固的把握,关于什么是闭包,但我想不出一个如何使面向对象的语言“更好”的坚实范例。

那么,大多数使用术语“闭包”的人实际上是指“函数对象”,从这个意义上说,函数对象可以在某些情况下编写更简单的代码,例如当您需要在sorting函数中使用自定义比较器时。

例如,在Python中:

 def reversecmp(x, y): return y - x a = [4, 2, 5, 9, 11] a.sort(cmp=reversecmp) 

这通过传递自定义比较functoin reversecmp来以相反的顺序对列表进行sorting。 lambda运算符的join使事情变得更加紧凑:

 a = [4, 2, 5, 9, 11] a.sort(cmp=lambda x, y : y - x) 

Java没有函数对象,所以它使用“仿函数类”来模拟它们。 在Java中,通过实现Comparator类的自定义版本并将其传递给sort函数来执行等效的操作:

 class ReverseComparator implements Comparator { public compare(Object x, Object y) { return (Integer) y - (Integer) x; } ... List<Integer> a = Arrays.asList(4, 2, 5, 9, 11); Collections.sort(a, new ReverseComparator()); 

正如你所看到的,它给出了与闭包相同的效果,但是更笨拙和更冗长。 然而,匿名内部类的增加避免了大部分的痛苦:

 List<Integer> a = Arrays.asList(4, 2, 5, 9, 11); Comparator reverse = new Comparator() { public Compare(Object x, Object y) { return (Integer) y - (Integer) x; } } Collections.sort(a, reverse); 

所以我想说,Java中的函子类+匿名内部类的组合足以弥补真正的函数对象的缺失,使得它们不必要的添加。

Java从1.1开始就已经closures了,只是非常繁琐和有限的方式。

只要有一些描述的callback,它们通常都是有用的。 一个常见的情况是抽象出控制stream,留下有趣的代码来调用一个没有外部控制stream的闭包。

一个简单的例子是for-each(虽然Java 1.5已经有了)。 虽然你可以用Java实现一个forEach方法,但它太冗长了。

现有的Java已经有了一个合理的例子,就是实现“执行周围”的习惯用法,即资源获取和释放是抽象的。 例如,文件的打开和closures可以在try / finally中完成,而不需要客户端代码来获取细节。

当闭包最终到达Java时,我会愉快地摆脱所有的自定义比较类。

 myArray.sort( (a, b) => a.myProperty().compareTo(b.myProperty() ); 

…看起来比…更好

 myArray.sort(new Comparator<MyClass>() { public int compare(MyClass a, MyClass b) { return a.myProperty().compareTo(b.myProperty(); } }); 

有几个人已经说或暗示闭包只是语法上的糖 – 做你已经可以用匿名内部类来做的事情,并且使得传递参数变得更加方便。

它们语法糖,就像Java是汇编程序的语法糖一样(为了说明起见,“汇编程序”可以是字节码)。 换句话说,他们提高了抽象层次,这是一个重要的概念。

闭包促进了作为对象的函数的概念到第一类实体 – 这增加了代码的performance力,而不是用更多的样板来混淆它。

Tom Hawtin已经提到了一个接近我心的例子 – 执行Execute Around idiom,这是将RAII变成Java的唯一方法。 几年前,当我第一次听到closures的时候,我写了一篇关于这个主题的博客文章 。

具有讽刺意味的是,我认为closures对Java有好处的原因(更lessperformance力,更less的代码)可能是许多Java倡导者的震撼。 Java有一个“拼出一条龙”的思路。 事实上,closures是一种更有效的做事方式 – 我也将它视为一件好事,但可能会淡化Java社区中许多人所珍视的纯粹的面向对象的信息。

在过去的几天里,我一直在思考这个非常有趣的问题 。 首先,如果我理解正确,Java已经有一些基本的closures概念(通过匿名类定义),但是将要引入的新特性是支持基于匿名函数的closures。

这个扩展肯定会使语言更具performance力,但我不确定它是否真的符合其他语言。 Java被devise成面向对象的语言,不支持函数式编程:新的语义会容易理解吗? Java 6甚至没有函数,Java 7会有匿名函数,但没有“正常”函数?

我的印象是,随着新的编程风格或函数式编程等范例变得越来越stream行,每个人都希望使用他们最喜欢的OOP语言。 这是可以理解的:人们希望继续使用他们熟悉的语言,同时采用新的function。 但是这样一种语言就会变得非常复杂,失去连贯性。

所以我现在的态度是坚持使用Java 6 for OOP(我希望Java 6仍然会支持一段时间),如果我真的有兴趣做OOP + FP,看看其他的语言,比如Scala(Scala从一开始就被定义为多范型,可以很好地与Java集成),而不是转换到Java 7。

我认为Java的成功归功于它将简单的语言和非常强大的库和工具结合起来,我不认为像闭包这样的新function会使它成为更好的编程语言。

现在JDK8即将发布,有更多的信息可以丰富这个问题的答案。

Oracle的语言架构师Bria Goetz发表了一系列关于Java中lambdaexpression式状态的文章(尚未草案)。 它还涵盖了计划在即将到来的JDK中发布的闭包,这些代码应在2013年1月左右完成,并应在2013年中左右发布。

  • Lambda状态 :在本文的第一或第二页中,本文试图回答这里提出的问题。 尽pipe我仍然认为它的论据很简短,但却充满了实例。
  • Lambda – Libraries的版本 :这也是非常有趣的,因为它涵盖了懒惰评估和并行性等优点。
  • Lambdaexpression式的翻译 :这基本上解释了Java编译器所做的解构过程。

作为一个正在努力教自己的java开发人员试图成为一个更好的程序员,我想说我想看看Josh Block提出的closures实施的build议。 我发现自己使用匿名内部类来expression一些东西,比如聚合一些数据时,如何处理列表中的每个元素。 将它作为闭包来表示,而不是必须创build一个抽象类。

命令式语言中的闭包(例如:JavaScript,C#,即将到来的C ++刷新)与匿名内部类不一样。 他们需要能够捕获对本地variables的可修改引用。 Java的内部类只能捕获本地的finalvariables。

几乎所有的语言特征都可以被批评为非必要的:

  • forwhile ,都是只是句法糖goto / if
  • 内部类是句法上的糖类,指向外部类的字段。
  • generics是语法上的糖。

完全相同的“非必要”论据本应阻止所有上述特征的包含。

Javaclosures示例

不仅那个benjismith,但是我爱你怎么能做…

myArray.sort {it.myProperty}

您只需要比较自然语言比较的财产不适合您的需要更详细的比较。

我绝对喜欢这个function。

可读性和可维护性怎么样?单线封闭是难以理解和debugging的,因此软件拥有更多的生活,并且可以让人们对这个语言有基本的了解来维护它…所以,比单线程为了方便维护…你通常没有一个软件明星在软件发布后对其进行pipe理。

您可能想看看Groovy,这是一种与Java大部分兼容的语言,并且在JRE上运行,但支持闭包。

在匿名函数中缺less绑定[即,如果外部上下文的variables(和方法参数,如果有一个封闭的方法)被声明为最终的,那么它们是可用的,但不是],我不完全明白实际购买的限制。

无论如何,我总是大量使用“最终”。 所以,如果我的意图是在封闭内使用相同的对象,我确实将这些对象声明在封闭范围内。 但是让“closure [java aic]”只是通过构造函数传递引用的副本(实际上是如何实现的)会出现什么错误。

如果封闭要覆盖引用,那就这样吧; 它会这样做,而不会更改封闭范围所见的副本。

如果我们认为这会导致不可读的代码(例如,在构造函数调用aic的时候可能不是直截了当地看到对象引用是什么),那么至less让语法不那么冗长? Scala呢? Groovy的?