Java中的匿名类和闭包有什么区别?

看起来匿名类提供了闭包的基本功能,这是真的吗?


答案 1

几乎没有区别。事实上,有一句关于闭包和对象的老话。闭包是穷人的对象,而物品是穷人的封闭。就它们能做什么而言,两者都同样强大。我们只是在争论表现力。

在Java中,我们使用匿名对象对闭包进行建模。事实上,这里有一点历史,最初Java有能力修改外部范围而不使用final。这对于在本地方法范围内分配的对象工作正常,但是当涉及到基元时,这引起了很多争议。基元是在堆栈上分配的,因此为了让它们在外部方法的执行之后存活,Java必须在堆上分配内存并将这些成员移动到堆中。当时人们对垃圾回收非常陌生,他们不信任它,所以声称Java不应该在没有程序员明确指示的情况下分配内存。为了达成妥协,Java决定使用最终关键字。

http://madbean.com/2003/mb2003-49/

现在有趣的是,Java可以消除这种限制,并使用最终关键字可选,因为每个人都对垃圾回收器更满意,从语言的角度来看,它可以完全兼容。尽管解决此问题的方法很简单,但在匿名对象上定义实例变量,您可以根据需要修改这些变量。事实上,这可能是一种实现对局部范围的闭包样式引用的简单方法,方法是通过编译器将公共实例变量添加到匿名类,并重写源代码以使用这些变量而不是堆栈变量。

public Object someFunction() {
   int someValue = 0;

   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();
   return someValue;
}

将重写为:

 public Object someFunction() {
   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public int someValue = 0;

       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();

   // all references to someValue could be rewritten to 
   // use this instance variable instead.
   return implementation.someValue;
}

我认为人们抱怨匿名内部类的原因更多地与静态类型与动态类型有关。在Java中,我们必须为匿名类的实现者和接受匿名类的代码定义一个商定的接口。我们必须这样做,这样我们才能在编译时对所有内容进行类型检查。如果我们有第一类函数,那么Java将需要定义一个语法来声明方法的参数,并将类型返回为数据类型,以保持静态类型语言以确保类型安全。这几乎与定义接口一样复杂。(一个接口可以定义多个方法,声明一等类方法的语法仅适用于一个方法)。您可以将其视为一种简短的接口语法。在引擎盖下,编译器可以在编译时将短格式表示法转换为接口。

有很多事情可以对Java进行,以改善匿名类的体验,而无需放弃语言或进行重大手术。


答案 2

就它们都影响其他“私人”范围而言,在非常有限的意义上,是的。但是,存在如此多的差异,答案也可能是否定的。

由于 Java 缺乏将代码块作为真正的 R 值处理的能力,因此内部类无法像通常在延续中那样传递代码块。因此,完全缺少作为延续技术的闭包。

虽然要进行垃圾回收的类的生存期由持有内部类的人延长(类似于闭包在将变量反弹到闭包时保持活动状态),但Java通过绑定进行重命名的能力受到限制,以符合现有的Java语法。

为了允许线程使用Java的线程争用模型正确地踩踏彼此的数据,内部类被进一步限制,可以访问保证不会扰乱的数据,即最终的局部变量。

这完全忽略了其他内部类(也称为静态内部类),它们在感觉上略有不同。换句话说,它涉及关闭可以处理的一些项目,但不符合大多数人认为有必要称为关闭的最低要求。