关闭:为什么它们如此有用?

koe*_*oen 55 oop closures functional-programming

作为OO开发人员,也许我很难看到它的价值.他们给了什么附加价值?它们适合OO世界吗?

jal*_*alf 67

您可以将其视为类的泛化.

你的班级有一些州.它有一些方法可以使用的成员变量.

闭包只是一种让函数访问本地状态的更方便的方法.

您不必创建一个知道您希望函数使用的局部变量的类,而只需在现场定义函数,它就可以隐式访问当前可见的每个变量.

使用传统OOP语言定义成员方法时,其闭包是"此类中可见的所有成员".

具有"正确"闭包支持的语言只是概括了这一点,因此函数的闭包是"这里可见的所有变量".如果" here "是一个类,那么你有一个传统的类方法.

如果" here "在另一个函数中,那么你就有了函数式程序员所认为的闭包.您的函数现在可以访问父函数中可见的任何内容.

所以这只是一个概括,删除了"函数只能在类中定义"的愚蠢限制,但保持"函数可以看到任何变量在它们被声明的位置可见"的想法.

  • @newacct:完全没有.您可以使用相同的闭包轻松创建多个函数.只需在同一个地方创建它们,它们就会捕获相同的变量.但正如我所说,一个闭包捕获"在这里可见的任何东西",而一个类方法捕获"在这里可见的任何东西*在课堂上*.当然,我不是在谈论本地课程,它真的不是与"常规"课程有很大关系. (2认同)

Gre*_*zky 28

闭包不会给你任何额外的力量.

你可以用它们实现的任何东西,没有它们你都可以实现.

但它们非常适用于使代码更清晰,更易读.而且我们都知道干净可读的短代码是一个更容易调试并且包含更少错误的代码.

让我举几个可能用法的简短Java示例:

    button.addActionListener(new ActionListener() {
        @Override public void actionPerformed(ActionEvent e) {
            System.out.println("Pressed");
        }
    });
Run Code Online (Sandbox Code Playgroud)

将被替换(如果Java有闭包):

button.addActionListener( { System.out.println("Pressed"); } );
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是一流函数的一个例子,而不是闭包.闭包也意味着可变捕获. (39认同)
  • 如果样式/可读性不是强大的,那么我们就可以用穿孔卡在世界上编写任何应用程序. (18认同)
  • 他们没有给你任何额外的力量 - 但他们确实给你额外的风格......我知道我更喜欢写和读你的第二个例子而不是前者.如果你更进一步考虑像scala这样的语言中的map(),filter()和fold()方法,而不是它们的java替代方案,那么样式就变得很明显了 (10认同)
  • 在这个游戏上稍稍迟了一点,但是我必须尊重地不同意闭包不会给你任何额外的力量.在多线程环境中,能够执行闭包以捕获匿名函数中的本地范围变量,然后在匿名函数执行时退出本地范围是非常强大的. (6认同)
  • 这个例子怎么封闭?我认为闭包必须引用封闭范围内的变量...... (5认同)
  • @Nick,@ Viktor:在Java中,您可以获得对当前作用域中任何变量的只读访问权限.使用真正的闭包,您可以获得读写访问权限.Scala等语言提供了读写的"var"引用,而不是只读的"val"引用.这对可维护代码有很大帮助. (3认同)

JW.*_*JW. 28

对我来说,闭包的最大好处是当你编写启动任务的代码,让任务运行,并指定任务完成时应该发生什么.通常,在任务结束时运行的代码需要访问开头可用的数据,并且闭包使这变得容易.

例如,JavaScript中的常见用法是启动HTTP请求.谁开始它可能想要控制响应到来时发生的事情.所以你会做这样的事情:

function sendRequest() {
  var requestID = "123";
  Ext.Ajax.request({
    url:'/myUrl'
    success: function(response) {
      alert("Request " + requestID + " returned");
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

由于JavaScript的闭包,"requestID"变量被捕获在success函数内部.这显示了如何在同一位置编写请求和响应函数,并在它们之间共享变量.如果没有闭包,则需要将requestID作为参数传递,或者创建包含requestID和函数的对象.

  • 关闭现实世界的非常好的例子 (6认同)

Mar*_*tin 5

恕我直言它归结为能够捕捉到的代码块他们的背景下,在一些点以后参考,如果按照要求/时/执行.

它们似乎不是什么大不了的事情,并且闭包绝对不是你每天要完成任务所需要的东西 - 但它们可以使代码更简单,更清晰地编写/管理.

[编辑 - 基于上述评论的代码示例]

Java的:

List<Integer> numbers = ...;
List<Integer> positives = new LinkedList<Integer>();
for (Integer number : integers) {
    if (number >= 0) {
        positives.add(number);
    }
}
Run Code Online (Sandbox Code Playgroud)

Scala(跳过一些其他细节,如类型推断和通配符,所以我们只是比较闭包的效果):

val numbers:List[Int] = ...
val positives:List[Int] = numbers.filter(i:Int => i >= 0)
Run Code Online (Sandbox Code Playgroud)


bla*_*999 5

很遗憾,人们不再在edu中学习Smalltalk;在那里,闭包用于控制结构、回调、集合枚举、异常处理等。对于一个很好的小例子,这里是一个工作队列操作处理程序线程(在 Smalltalk 中):

|actionQueue|

actionQueue := SharedQueue new.
[
    [
        |a|

        a := actionQueue next.
        a value.
    ] loop
] fork.

actionQueue add: [ Stdout show: 1000 factorial ].
Run Code Online (Sandbox Code Playgroud)

并且,对于那些看不懂 Smalltalk 的人来说,JavaScript 语法也是一样的:

var actionQueue;

actionQueue = new SharedQueue;
function () {
    for (;;) {
        var a;

        a = actionQueue.next();
        a();
    };
}.fork();

actionQueue.add( function () { Stdout.show( 1000.factorial()); });
Run Code Online (Sandbox Code Playgroud)

(好吧,如您所见:语法有助于阅读代码)

编辑:注意 actionQueue 是如何从块内部引用的,它甚至适用于分叉的线程块。这就是使闭包如此易于使用的原因。