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

时间:2011-06-20 14:10:58

标签: java closures terminology anonymous-class

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

3 个答案:

答案 0 :(得分:14)

几乎没有区别。事实上,关于封闭物和物体有一句老话。关闭是穷人的目标,对象是穷人的关闭。两者在他们能做的事情上同样强大。我们只是在争论表达力。

在Java中,我们使用匿名对象建模闭包。事实上,这里有一点历史,原来Java有能力在不使用final的情况下修改向外范围。这适用于在本地方法范围内分配的对象,但是当涉及到原语时,这引起了很多争议。基元是在堆栈上分配的,因此为了使它们能够超越外部方法的执行,Java必须在堆上分配内存并将这些成员移动到堆中。当时人们对垃圾收集非常新,他们不相信它所以声称Java没有在没有程序员明确指示的情况下分配内存。为了达成妥协,Java决定使用final关键字。

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

现在有趣的是,Java可以删除该限制并使用final关键字可选,因为现在每个人都对垃圾收集器更加熟悉,并且从语言角度来看它可以完全兼容。虽然解决此问题很容易在匿名对象上定义实例变量,但您可以根据需要修改它们。实际上,通过编译器将公共实例变量添加到匿名类,并重写源代码以使用它们而不是堆栈变量,这可能是实现对本地范围的闭包样式引用的简单方法。

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

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

在不放弃语言或大手术的情况下,Java可以做很多事情来改善匿名类体验。

答案 1 :(得分:9)

至于它们都会影响“私人”范围,在非常有限的意义上,是的。然而,有很多不同之处,答案可能都是否定的。

由于Java缺乏将代码块作为真正的R值处理的能力,因此内部类不能像通常在continuation中那样传递代码块。因此,封闭作为一种延续技术是完全缺失的。

虽然垃圾收集类的生命周期是由持有内部类的人扩展的(类似于闭包保持变量活跃而反弹到闭包),但Java通过绑定进行重命名的能力仅限于符合现有的Java语法。

为了让线程能够使用Java的线程争用模型正确地压缩彼此的数据,内部类进一步受限于访问保证不会沮丧的数据,即最终的本地化。

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

答案 2 :(得分:4)

恕我直言,他们提供了类似的目的,但封闭旨在更简洁,并可能提供更多功能。

假设您要使用匿名类来使用局部变量。

final int[] i = { 0 };
final double[] d = { 0.0 };

Runnable run = new Runnable() { 
    public void run() {
        d[0] = i[0] * 1.5;
    }
};
executor.submit(run);

通过允许您只写出预期的内容,闭包避免了对大多数锅炉板编码的需要。

int i = 0;
double d = 0.0;

Runnable run = { => d = i * 1.5; };
executor.submit(run);

甚至

executor.submit({ => d = i * 1.5; });

或者闭包是否支持代码块。

executor.submit() {
    d = i * 1.5; 
}
相关问题