Java是否需要闭包?

时间:2020-03-05 18:49:31  来源:igfitidea点击:

最近,我一直在阅读有关下一版Java可能支持闭包的内容。我感觉我对什么是闭包有一个非常坚定的把握,但是我想不出一个可靠的例子说明它们如何使"面向对象"语言变得"更好"。谁能给我一个特定的用例,其中需要关闭(或者什至首选)?

解决方案

回答

最明显的是,对于所有这些类,只有一个名为run()或者actionPerformed()或者类似方法的类,将进行伪替换。因此,我们可以使用闭包代替创建带有嵌入式Runnable的线程。没有我们现在拥有的强大,但是更加方便和简洁。

那么我们需要关闭吗?不,他们会很高兴吗?当然,只要它们不会像我担心的那样发狂。

回答

我想支持核心功能编程概念,我们需要闭包。支持闭包,使代码更加优雅和可组合。另外,我喜欢将代码行作为参数传递给函数的想法。

回答

Java闭包示例

回答

他们没有使面向对象的语言更好。它们使实用语言更加实用。

如果我们正在使用OO锤攻击问题,则将所有事物都表示为对象之间的相互作用,那么闭包是没有意义的。在基于类的OO语言中,封闭是烟雾弥漫的后室,那里的工作完成了,但之后没人再谈论它。从概念上讲,这是可恶的。

实际上,这非常方便。我真的不想定义一种新类型的对象来保存上下文,为它建立"填充"方法,实例化它,然后填充上下文...我只想告诉编译器,"看看,看看有什么我现在可以访问吗?这是我想要的上下文,这是我要使用它的代码,直到我需要它为止。

很棒的东西。

回答

从1.1开始,Java就已经关闭了,只是一种非常繁琐且有限的方式。

只要我们有一些描述的回调,它们通常就很有用。一个常见的情况是抽象出控制流,留下有趣的代码来调用带有外部控制流的闭包的算法。

每个示例都是一个简单的示例(尽管Java 1.5已经拥有了)。虽然我们可以按现状使用Java实现forEach方法,但它太冗长而无用。

现有Java已经有意义的一个示例是实现"环顾四周"惯用语,从而抽象化资源的获取和释放。例如,可以在try / final内完成文件打开和关闭,而客户端代码不必正确获取细节。

回答

作为一个Java开发人员,他试图自学Lisp试图成为一个更好的程序员,我想说我希望看到Josh Block关于闭包的建议得以实施。我发现自己使用匿名内部类来表达诸如聚合某些数据时如何处理列表中的每个元素之类的内容。最好将其表示为闭包,而不必创建抽象类。

回答

Java不需要闭包,面向对象语言可以使用中间对象存储状态或者执行操作(在Java的内部类中)来完成闭包所做的所有事情。
但是闭包是一种理想的功能,因为闭包可以极大地简化代码并提高可读性,从而提高代码的可维护性。

我不是Java专家,但我使用的是C3.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);
    }
}

我知道这是Cand而不是Java,但是想法是相同的,闭包对于简洁起了作用,并且使代码更短且更具可读性。在幕后,C3.5的闭包完成了与示例2极为相似的操作,这意味着编译器在幕后创建了一个私有类,并将'filter'参数传递给了它。

Java不需要闭包来工作,作为开发人员,我们也不需要闭包,但是,它们很有用并提供好处,这意味着它们在生产语言中是理想的,并且其目标之一就是提高生产力。 。

回答

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

例如。对列表中的每个元素进行某种转换是非常常见的操作。这种较高阶的函数通常称为"地图"或者"收集"。 (请参阅*。Groovy的传播运算符)。

例如,将列表中的每个元素平方成正方形而不包含闭包,我们可能会这样写:

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解释的那样,还有另一个名为"选择"或者"过滤器"的高阶函数:可用于获取列表中符合某些条件的所有元素。例如:

代替:

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用户指南》中有关收集的章节。

备注:

关于"关闭"一词,可能需要一些澄清。我上面显示的内容严格来说是没有闭包的。它们只是"功能对象"。
闭包是可以捕获或者"封闭"其周围代码的(词法)上下文的所有内容。从这种意义上讲,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中时,我会很高兴地摆脱所有自定义比较器类。

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();
   }
});

回答

不仅是本杰史密斯,而且我爱你可以做到的...

myArray.sort {it.myProperty}

当属性的自然语言比较不符合需求时,我们只需要显示的更详细的比较器即可。

我绝对喜欢这个功能。

回答

I've been reading a lot lately about the next release of Java possibly supporting closures. I feel like I have a pretty firm grasp on what closures are, but I can't think of a solid example of how they would make an Object-Oriented language "better."

好吧,大多数使用"闭包"一词的人实际上是"功能对象",从这个意义上说,在某些情况下,例如当我们需要在排序功能中使用自定义比较器时,功能对象使编写更简单的代码成为可能。

例如,在Python中:

def reversecmp(x, y):
   return y - x

a = [4, 2, 5, 9, 11]
a.sort(cmp=reversecmp)

通过传递自定义比较functoin reversecmp,可以以相反的顺序对列表进行排序。 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中函子类+匿名内部类的组合足以弥补真正函数对象的不足,从而不必添加它们。

回答

作为一名Lisp程序员,我希望Java社区理解以下区别:函数作为对象vs.闭包。

a)函数可以命名或者匿名。但是它们也可以成为自己的对象。这允许函数作为参数传递,从函数返回或者存储在数据结构中。这意味着函数是编程语言中的一流对象。

匿名函数并没有为语言增加太多,它们只是允许我们以较短的方式编写函数。

b)闭包是一个函数加上一个绑定环境。闭包可以向下传递(作为参数),也可以向上传递(作为返回值)。即使周围的代码不再处于活动状态,这也允许该函数引用其环境的变量。

如果我们有使用某种语言的a),那么接下来的问题便是如何处理b)?有些语言有a),但没有b)。在函数式编程世界中,a)(函数)和b(函数作为闭包)已成为当今的规范。 Smalltalk拥有a)(块是匿名函数)已经很长时间了,但是随后Smalltalk的一些方言增加了对b)(块作为闭包)的支持。

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

从实用的角度来看,匿名函数会添加一些简短的符号,我们可以在其中传递或者调用函数。那可能是一件好事。

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

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

回答

强制性语言的闭包(例如:JavaScript,C#,即将推出的C ++刷新)与匿名内部类不同。他们需要能够捕获对局部变量的可修改引用。 Java的内部类只能捕获局部final变量。

几乎所有语言功能都可以被批评为非必要功能:

  • forwhiledo都是goto/if上的语法糖。
  • 内部类是指向类的语法糖,其字段指向外部类。
  • 泛型是语法转换糖。

完全相同的"非必要"参数应该阻止了所有上述功能的包含。

回答

少数人已经说过或者暗示说,闭包只是语法糖,它可以完成匿名内部类已经可以做的事情,并使传递参数更方便。

它们是语法糖,就象Java是汇编程序的语法糖一样(出于争论,"汇编程序"可以是字节码)。换句话说,它们提高了抽象水平,这是一个重要的概念。

闭包将功能对象的概念提升为一流的实体,该实体增加了代码的表达能力,而不是使代码变得更加繁琐。

Tom Hawtin实现了Execute Around习惯用语已经提到了一个与我密切相关的示例,这几乎是将RAII引入Java的唯一方法。几年前,当我第一次听说关闭即将到来时,我写了一篇有关该主题的博客文章。

具有讽刺意味的是,我认为闭包对Java有利的原因(更少的代码可以提高表达能力)可能是引起许多Java拥护者的不满。 Java的思维定式是"将所有内容都尽力拼写"。这样的事实以及闭包是一种朝着更实用的做事方式致敬的事实,我也认为这是一件好事,但可能会削弱Java社区中许多人珍视的纯OO信息。

回答

可读性和可维护性呢...单行闭包更难理解和调试,imo
软件生命长久,我们可以让拥有基本语言知识的人员对其进行维护...因此,比单行代码更好地传播逻辑以便于维护...通常没有软件明星会在软件发布后照看软件释放...