闭包和传统类之间有什么区别?

10

使用闭包和类的优缺点,以及两者之间的比较如何?

编辑:
正如用户Faisal所说,闭包和类都可以用于“描述维护和操作状态的实体”,因此使用闭包提供了一种使用函数式语言进行面向对象编程的方法。像大多数程序员一样,我更熟悉类。

本问题的目的不是要再次引发关于哪种编程范例更好,或者闭包和类是否完全等效,或是互相替代的争论。

我想知道的是是否有人发现其中一种方法在某种情况下真正超过了另一种方法,并且为什么。


1
回答这个问题并不容易,因为以语言无关的方式来回答它非常困难,因为最方便的方式几乎必然取决于你使用的编程语言。 - zneak
3个回答

13

从功能上讲,闭包和对象是等效的。闭包可以模拟对象,反之亦然。因此,你使用哪个取决于语法方便性或者你所使用的编程语言最好处理哪个。

在C++中,闭包在语法上不可用,因此你必须使用“函数对象”,这些对象重载了operator(),并且可以以看起来像函数调用的方式进行调用。

在Java中,甚至没有函数对象,因此你会得到像访问者模式这样的东西,在支持闭包的语言中,它只是一个高阶函数。

在标准Scheme中,你没有对象,因此有时你需要通过编写带有分发函数的闭包来实现它们,根据传入的参数执行不同的子闭包。

在像Python这样既有函数对象又有闭包语法的语言中,基本上是一种口味和你感觉哪种更好地表达了你正在做的事情。

个人而言,在任何同时具有两者语法的语言中,闭包都是表达具有单个方法的对象更清晰、更简洁的方式。反之,如果你的闭包开始根据传入的参数处理分发到子闭包,那么你应该使用对象。


4
个人认为,这是使用正确的工具来完成任务的问题...更具体地说,是适当地传达您的意图。
如果您想明确显示所有对象共享公共定义,并希望进行此类强类型检查,则可能需要使用类。在这种情况下,无法在运行时更改类的结构的缺点实际上是一种优势,因为您知道自己正在处理什么。
相反,如果您想创建一个“对象”的异构集合(即状态表示为变量,闭合在某些函数下/内部函数操作该数据),则最好创建一个闭包。在这种情况下,对于您最终得到的对象的结构没有真正的保证,但是您可以在运行时完全自由地定义它。
实际上,谢谢你的提问;起初我有点膝反应,认为“类和闭包完全不同!”,但是经过一些研究,我意识到问题并没有我想象的那么简单明了。

换句话说,闭包作为对象更像大多数面向对象编程语言中的接口——内部表示完全隐藏,任何提供相同公共方法的内容都可以进行交换。有些人实际上认为这是更好的面向对象编程风格,令人感到有趣... - C. A. McCann

1
闭包与类之间关系很微弱。类允许您定义字段和方法,而闭包则保存有关函数调用中的局部变量的信息。无法以与语言无关的方式进行两者的比较:它们根本不具备相同的目的。此外,闭包与函数式编程比与面向对象编程更相关。
例如,看一下以下C#代码:
static void Main(String[] args)
{
    int i = 4;
    var myDelegate = delegate()
    {
        i = 5;
    }

    Console.WriteLine(i);
    myDelegate();
    Console.WriteLine(i);
}

这将返回"4"然后是"5"。myDelegate作为一个代理,是一个闭包并且知道函数当前使用的所有变量。因此,当我调用它时,它可以更改“父”函数内i的值。对于普通函数,这是不允许的。

如果您知道类是什么,那么它们完全不同。

您困惑的可能原因是,当一种语言没有对闭包提供语言支持时,可以使用类来模拟闭包,以保留我们需要保留的每个变量。例如,我们可以像这样重写上面的代码:

class MainClosure()
{
    public int i;

    void Apply()
    {
        i = 5;
    }
}

static void Main(String[] args)
{
    MainClosure closure;
    closure.i = 4;

    Console.WriteLine(closure.i);
    closure.Apply();
    Console.WriteLine(closure.i);
}

我们已经将delegate转换为一个名为MainClosure的类。我们没有在Main函数内部创建变量i,而是创建了一个MainClosure对象,该对象具有i字段。这是我们将使用的字段。此外,我们已经将函数执行的代码构建到实例方法中,而不是方法内部。
正如您所看到的,即使这只是一个简单的示例(仅有一个变量),它仍需要相当多的工作。在需要闭包的上下文中,使用对象是一种较差的解决方案。然而,类不仅用于创建闭包,它们通常的目的通常远不同。

1
我也曾这样想,但是关于闭包和对象哪个更具表现力的争论似乎还有很多。让我给你们介绍一下这个讨论的味道:http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html - Faisal
根据我的理解,您可以通过闭包实现对象,并且可以通过对象模拟闭包。但是,对象通常不会自动捕获本地环境,因此您必须明确初始化它们...但是@zneak是正确的:它们最终服务于两个不同的目的,同时想要两者都具备并不是不合理的。 - Amadan
@zneak:如果闭包作为一种语言结构可用,为什么要使用类来模拟闭包?不过我同意你得出的结论是显而易见的。 :) - Faisal
@Faisal:如果不必要,您不想使用类来模拟闭包。那很愚蠢。我举了这个例子是因为C#支持两者,并且更容易展示比较(有闭包时和没有闭包时的情况)。 - zneak
闭包是对象的超集,因为它们可以表示为一个类的实例,其中每个捕获变量都有一个字段和一个单独的“apply”方法。其他部分都是语法糖(虽然非常有用的语法糖)。 - zneak
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接