我最近一直在阅读关于Java的下一个版本可能支持closures的内容。我觉得我对封闭是什么有一个非常牢固的把握,但我想不出一个如何使面向对象语言“更好”的一个可靠的例子。任何人都可以给我一个特定的用例,需要关闭(甚至是首选)?
答案 0 :(得分:70)
作为一名Lisp程序员,我希望Java社区能够理解以下差异:作为对象与闭包的功能。
a)功能可以命名或匿名。但它们也可以成为自己的对象。这允许函数作为参数传递,从函数返回或存储在数据结构中。这意味着函数是编程语言中的第一类对象。
匿名函数不会对语言增加太多,它们只允许您以较短的方式编写函数。
b)闭包是一个功能加上一个绑定环境。闭包可以向下传递(作为参数)或返回向上(作为返回值)。这允许函数引用其环境的变量,即使周围的代码不再有效。
如果你有某种语言的 a),那么问题就出现了怎么办 b)?有些语言有 a),但 b)。在函数式编程世界中, a)(函数)和 b (函数作为闭包)现在是常态。 Smalltalk很长一段时间都有 a)( blocks 是匿名函数),但是Smalltalk的一些方言增加了对 b)的支持(块为封闭件)。
如果你在语言中添加函数和闭包,你可以想象你会得到一个稍微不同的编程模型。
从实用的角度来看,匿名函数添加了一些简短的表示法,您可以在其中传递或调用函数。这可能是一件好事。
闭包(函数加绑定)允许您创建一个可以访问某些变量的函数(例如,计数器值)。现在,您可以将该函数存储在对象中,访问它并调用它。函数对象的上下文现在不仅是它有权访问的对象,而且还有它可以通过绑定访问的变量。这也很有用,但你可以看到变量绑定与对象变量的访问现在是一个问题:什么时候应该是 lexical 变量(可以在闭包中访问)以及什么时候应该是某个对象的变量( slot )。什么时候应该是一个闭包或一个对象?您可以以类似的方式使用它们。学生学习Scheme(一种Lisp方言)的常用编程练习是使用闭包编写一个简单的对象系统。
结果是更复杂的编程语言和更复杂的运行时模型。太复杂了?
答案 1 :(得分:41)
他们没有更好地使面向对象的语言。它们使实用语言更加实用。
如果你正在攻击OO锤子的问题 - 将所有东西都表示为对象之间的相互作用 - 那么闭合就没有意义了。在基于类的OO语言中,闭包是充满烟雾的后室,其中的东西已经完成,但之后没有人谈论它。从概念上讲,这是令人憎恶的。
在实践中,它非常方便。我真的不想定义一个新类型的对象来保存上下文,为它建立“do stuff”方法,实例化它,并填充上下文...我只是想告诉编译器,“看,看看是什么我现在可以访问?这是我想要的上下文,这里是我想用它的代码 - 为我保留这个'直到我需要它“。
很棒的东西。
答案 2 :(得分:36)
最明显的事情是所有那些只有一个名为run()或actionPerformed()或类似方法的类的伪替换。因此,不是创建一个嵌入了Runnable的Thread,而是使用闭包。不比我们现在拥有的更强大,但更方便和简洁。
那么我们需要关闭吗?不,他们会很高兴吗?当然,只要他们不觉得狂奔,我担心他们会这样。
答案 3 :(得分:18)
我想支持核心函数式编程概念,需要闭包。通过对closures的支持,使代码更加优雅和可组合。另外,我喜欢将代码行作为参数传递给函数。
答案 4 :(得分:12)
有一些非常有用的“高阶函数”可以使用闭包对列表进行操作。高阶函数是具有“函数对象”作为参数的函数。
E.g。将一些转换应用于列表中的每个元素是一种非常常见的操作。这种高阶函数通常称为“map”或“collect”。 (参见Groovy的*。spread运算符。)
例如,为了对没有闭包的列表中的每个元素进行平方,您可能会写:
List<Integer> squareInts(List<Integer> is){
List<Integer> result = new ArrayList<Integer>(is.size());
for (Integer i:is)
result.add(i*i);
return result;
}
使用闭包和贴图以及建议的语法,你可以这样写:
is.map({Integer i => i*i})
(关于原始类型的装箱,这里可能存在性能问题。)
正如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});
使用提案。
您可以查看Groovy语法,它是Java语言的扩展,目前支持闭包。有关如何处理闭包的更多示例,请参阅有关Groovy User Guide集合的章节。
一句话:
关于“关闭”一词可能需要澄清一些。我上面所说的是严格说的没有封闭。它们只是“功能对象”。 闭包是可以捕获 - 或“关闭” - 围绕它的代码的(词汇)上下文的一切。从这个意义上讲,Java现在有一些闭包,即匿名类:
Runnable createStringPrintingRunnable(final String str){
return new Runnable(){
public void run(){
System.out.println(str); // this accesses a variable from an outer scope
}
};
}
答案 5 :(得分:8)
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非常相似,这意味着编译器在幕后创建了一个私有类,并将'filter'参数传递给它。
Java不需要闭包工作,作为一个你也不需要它们的开发人员,但是,它们很有用并且提供了好处,这意味着它们在一种生产语言的语言中是可取的,其中一种是目标是生产力。
答案 6 :(得分:8)
我最近一直在阅读关于下一版Java可能支持闭包的内容。我觉得我对封闭是什么有一个非常牢固的把握,但我想不出一个如何使面向对象语言“更好”的一个可靠的例子。
嗯,大多数使用术语“闭包”的人实际上是指“函数对象”,从这个意义上说,函数对象可以在某些情况下编写更简单的代码,例如在sort函数中需要自定义比较器时。
例如,在Python中:
def reversecmp(x, y):
return y - x
a = [4, 2, 5, 9, 11]
a.sort(cmp=reversecmp)
通过传递自定义比较functoin reversecmp,以相反的顺序对列表a进行排序。添加lambda运算符使事情变得更加紧凑:
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中的仿函数类+匿名内部类的组合足以弥补真正的函数对象的缺乏,使得它们的添加变得不必要。
答案 7 :(得分:5)
自1.1以来,Java一直处于封闭状态,只是处于一种非常繁琐且有限的方式。
如果您有某些描述的回调,它们通常很有用。一个常见的情况是抽象出控制流,让有趣的代码调用一个没有外部控制流的闭包的算法。
一个简单的例子是for-each(尽管Java 1.5已经有了)。虽然你可以用Java实现一个forEach方法,但是它太冗长而且无用。
现有Java已经有意义的一个例子是实现“执行”惯用法,从而抽象出资源获取和释放。例如,文件打开和关闭可以在try / finally中完成,而客户端代码不必正确地获取详细信息。
答案 8 :(得分:3)
当闭包最终到达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();
}
});
答案 9 :(得分:3)
有些人已经或者暗示,闭包只是语法糖 - 做了你已经可以用匿名内部类做的事情,并且让传递参数更方便。
他们是语法糖,就像Java是汇编程序的语法糖一样(为了参数,“汇编程序”可能是字节码)。换句话说,他们提高了抽象水平,这是一个重要的概念。
Closures将函数作为对象的概念提升为第一类实体 - 一种增加代码表现力的实体,而不是用更多的样板来混淆它。
Tom Hawtin已经提到了一个接近我心的例子 - 实现了Execute Around成语,这是将RAII变成Java的唯一方法。 I wrote a blog entry on exactly that subject几年前,当我第一次听到关闭可能会到来时。具有讽刺意味的是,我认为闭包对Java有好处的原因(用更少的代码表达更多)可能是许多Java倡导者的喋喋不休。 Java有一种“长期拼出一切”的心态。这个以及闭包是一种更加功能性的做事方式的事实 - 我也认为这是一件好事,但可能会淡化Java社区中许多人所珍视的纯OO信息。
答案 10 :(得分:3)
现在即将发布JDK8,有更多可用的信息可以丰富这个问题的答案。
Oracle的语言架构师Bria Goetz发表了一系列关于Java中lambda表达式当前状态的论文(还有草稿)。它还包括关闭,因为他们计划在即将推出的JDK中发布它们,这应该是代码完成around January 2013并且应该在2013年年中左右发布。答案 11 :(得分:3)
我一直在思考这个非常有趣的问题的主题 最近几天。首先,如果我理解正确,Java已经有了 闭包的一些基本概念(通过匿名类定义)但新功能 将要介绍的是基于匿名函数的闭包支持。
这个扩展肯定会使语言更具表现力,但我不确定 如果它真的适合其他语言。 Java被设计为面向对象的语言,不支持函数式编程:新的语义是否易于理解? Java 6甚至没有函数,Java 7是否具有匿名函数但没有“正常”函数?
我的印象是,作为新的编程风格或功能范式 编程变得越来越流行,每个人都想在他们的网站中使用它们 最喜欢的OOP语言。这是可以理解的:一个人想继续使用 他们在采用新功能时熟悉的语言。但就是这样 语言会变得非常复杂并失去连贯性。
所以我现在的态度是坚持使用Java 6进行OOP(我希望Java 6仍然可以 支持一段时间),如果我真的对OOP + FP感兴趣, 看看像Scala这样的其他语言(Scala被定义为多个 范式从一开始就可以很好地与Java集成)而不是切换 到Java 7。
我认为Java的成功归功于它将简单的语言与非常简单的语言相结合 强大的库和工具,我不认为像闭包这样的新功能会 使它成为一种更好的编程语言。
答案 12 :(得分:2)
作为一名java开发人员,他试图自学lisp以试图成为更好的程序员,我想说我希望看到Josh Block关于实现闭包的建议。我发现自己使用匿名内部类来表达在聚合某些数据时如何处理列表中每个元素的事情。很好地将它表示为闭包,而不是必须创建一个抽象类。
答案 13 :(得分:2)
命令式语言中的闭包(例如:JavaScript,C#,即将推出的C ++刷新)与匿名内部类不同。他们需要能够捕获对局部变量的可修改引用。 Java的内部类只能捕获本地final
变量。
几乎任何语言特征都可以被批评为非必要的:
for
,while
,do
只是goto
/ if
上的语法糖。完全相同的“非必要”论证应该阻止包含所有上述功能。
答案 14 :(得分:1)
答案 15 :(得分:0)
匿名函数缺乏约束力[即如果外部上下文的变量(和方法参数,如果有一个封闭方法)被声明为 final ,那么它们是可用的,但不是其他的,我不太明白这个限制实际上是什么买的。 / p>
无论如何我大量使用“最终”。所以,如果我的意图是在闭包内使用相同的对象,我确实会在封闭范围内声明这些对象为final。但是让“闭包[java a.i.c.]”只是获取引用的副本就好像通过构造函数传递一样(事实上它是如何完成的)。
如果闭包想要覆盖引用,那就这样吧;它会在不更改封闭范围看到的副本的情况下这样做。
如果我们认为这会导致无法读取的代码(例如,在构造函数调用aic时可能不能直接看到对象引用是什么),那么至少如何使语法更简洁? Scala呢? Groovy的?
答案 16 :(得分:0)
您可能希望查看Groovy,这是一种与Java兼容的语言,并在JRE上运行,但支持Closures。
答案 17 :(得分:0)
可读性和可维护性如何......单线封闭更难理解和调试,imo 软件具有很长的使用寿命,你可以让人们掌握语言的基本知识来维护它...因此,为了便于维护,分散逻辑比单行更好...你通常没有一个软件明星照顾它后面的软件释放...
答案 18 :(得分:0)
不仅是那个benjismith,而且我喜欢你能做到的......
myArray.sort {it.myProperty}
当物业的自然语言比较不符合您的需求时,您只需要更详细的比较器。
我非常喜欢这个功能。