【问题标题】:Java "Closures" comparison between Local and Anonymous Class本地类和匿名类之间的 Java“闭包”比较
【发布时间】:2012-09-23 19:13:31
【问题描述】:

我已经对使用匿名类和本地类实现闭包的区别进行了一些搜索。我正在尝试找出两者之间的所有差异,以便知道哪种方法在哪种情况下更好。

如果我错了,请纠正我:

  • 每次创建新实例时,匿名类都会创建一个类实例和对象实例。
  • 每次创建新实例时,本地类只有一个对象实例。

因此,我是否应该在某个时间或地点使用匿名类而不是本地类?

编辑:两者之间似乎没有真正的区别,只是取决于样式以及是否要重用该类。

为了澄清我的意思,我举了一个例子:

public class ClosureExample {
interface Function {
    void func(int value);
}

public static void main(final String[] args) {
    final Function local1 = localClassClosure("Local1");
    final Function local2 = localClassClosure("Local2");

    final Function anonymous1 = anonymousClassClosure("Annonymous1");
    final Function anonymous2 = anonymousClassClosure("Annonymous2");

    for (int i = 0; i < 3; i++) {
        local1.func(i);
        local2.func(i);
        anonymous1.func(i);
        anonymous2.func(i);
    }
}

private static Function localClassClosure(final String text) {
    // Local class name is irrelevant in this example
    class _ implements Function {
        @Override public void func(final int value) {
            System.out.println(text + ":" + value);
        }
    }
    return new _();
}

private static Function anonymousClassClosure(final String text) {
    return new Function() {
        @Override public void func(final int value) {
            System.out.println(text + ":" + value);
        }
    };
}
}

希望有人能详细解释一下这种细微的差别,以及在什么情况下应该使用哪种方法。

【问题讨论】:

  • 为什么下划线是类名?
  • 类实例和对象实例是什么意思?
  • 通过类实例,我指的是类加载器正在加载到JVM中的类。
  • 请注意,本问题中讨论的 Java 8 之前的“闭包”在通常接受的意义上并不是真正的闭包。不同之处在于封闭范围内的变量必须都是final。这允许通过将这些变量的影子副本添加到内部/匿名类作为隐藏变量来模拟闭包行为。

标签: java closures


【解决方案1】:

这引起了我的兴趣,我打开 JD-GUI 来查看反编译的类。编译后的两个匿名内部类其实完全没有区别:

本地类:

class ClosureExample$1t implements ClosureExample.Function{
  ClosureExample$1t(String paramString){
  }

  public void func(int value){
    System.out.println(this.val$text + ":" + value);
  }
}

匿名类:

class ClosureExample$1 implements ClosureExample.Function{
  ClosureExample$1(String paramString){
  }

  public void func(int value){
    System.out.println(this.val$text + ":" + value);
  }
}

这两种方法都是实现匿名内部类的有效方法,而且它们的作用似乎完全相同。

编辑:我将 _ 类重命名为 t

【讨论】:

  • 啊,没想到反编译后看类。是的,很有趣其实没有区别!
【解决方案2】:

我很确定没有像对象实例这样的东西,只有类实例。 所以是的,为本地和匿名类型创建了一个对象.. 然而不同的是你不能重用匿名类(除非通过你在方法中使用它的方式——它有效但不是真正可维护的),所以当你做的任何事情都是一次性的事情时,你可以使用它。例如事件监听器。

我更喜欢命名类型而不是匿名类型。

You might find this useful

编辑: 你会发现我的question 在这里很有用。

【讨论】:

  • 我已经意识到我的误解在哪里,实际上并没有什么区别,而且我在某处读到过,每次创建匿名类实例时都会创建一个新类。事实并非如此。
  • 类只创建一次。编译器正在生成一个类,您只是不知道名称。它可能类似于“RandomClassABlah”
  • 直到我打印出正在创建的类名时才意识到这一点: ClosureExample$1_ ClosureExample$1_ ClosureExample$1 ClosureExample$1 我期待第二个匿名类是 ClosureExample$2
  • @Jyro117 你让我想起了同样的问题。查看我的更新。
【解决方案3】:

请注意:

因此,我是否应该在某个时间或地点使用匿名类而不是本地类?

如果您需要快速设置事件侦听器 [例如KeyListener] 在组件中,你可以这样做:

addKeyListener(new KeyListener(){

    public void keyPressed(KeyEvent ke){ ... }

    // further implementation here

});

虽然它根本不能重复使用。

【讨论】:

    【解决方案4】:
    1. 本地类对象在初始化时更快(因为该类在启动时已经在内存中)
    2. 匿名类对象的内存消耗更少(因为惰性求值)

    注意:因为 java 不是真正的函数式语言。匿名类将被预先评估,甚至存储在类文件中。所以真的不会有太大区别。

    在函数式语言中,例如方案:

    (define inc (lambda (a) (lambda () (+ 1 a))))
    (display ((inc 5))) 
    

    函数 (lambda () (+ 1 a)) 实际上会在每次匿名调用时重新创建,例如 ((inc 5))。这是匿名类背后的概念。

    相对于:

    (define inc (lambda (a) (+ 1 a)))
    (display (inc 5)) 
    

    其中 (lambda (a) (+ 1 a)) 将在编译时存储在内存中,对 (inc 5) 的调用只会引用它。这是本地类背后的概念。

    【讨论】:

    • 那么初始化后,这两种方式没有区别?
    • 差不多......因为据我所知,java实际上在运行时创建了类文件,即使是匿名类也是如此。 Java 并不是真正的函数式语言。在函数式语言中,这两种情况的区别是一个基本概念。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多