java如何实现内部类闭包?

发布于 2024-08-31 20:01:54 字数 387 浏览 6 评论 0原文

在 Java 中,匿名内部类可以引用其本地范围内的变量:

public class A {
    public void method() {
        final int i = 0;

        doStuff(new Action() {
            public void doAction() {
                Console.printf(i);   // or whatever
            }
        });
    }
}

我的问题是这实际上是如何实现的? i 如何获得匿名内部 doAction 实现,为什么它必须是 final

In Java an anonymous inner class can refer to variables in it's local scope:

public class A {
    public void method() {
        final int i = 0;

        doStuff(new Action() {
            public void doAction() {
                Console.printf(i);   // or whatever
            }
        });
    }
}

My question is how is this actually implemented? How does i get to the anonymous inner doAction implementation, and why does it have to be final?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

五里雾 2024-09-07 20:01:54

局部变量(显然)不在不同方法之间共享,例如上面的 method()doAction() 。但由于它是最终的,在这种情况下不会发生任何“坏”事情,因此语言仍然允许它。然而,编译器需要对这种情况采取一些巧妙的措施。让我们看看 javac 产生了什么:

$ javap -v "A\$1"           # A$1 is the anonymous Action-class.
...
final int val$i;    // A field to store the i-value in.

final A this$0;     // A reference to the "enclosing" A-object.

A$1(A, int);  // created constructor of the anonymous class
  Code:
   Stack=2, Locals=3, Args_size=3
   0: aload_0
   1: aload_1
   2: putfield #1; //Field this$0:LA;
   5: aload_0
   6: iload_2
   7: putfield #2; //Field val$i:I
   10: aload_0
   11: invokespecial #3; //Method java/lang/Object."<init>":()V
   14: return
   ...
public void doAction();
  Code:
   Stack=2, Locals=1, Args_size=1
   0: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: getfield #2; //Field val$i:I
   7: invokevirtual #5; //Method java/io/PrintStream.println:(I)V
   10: return

这实际上表明它将

  • i 变量转换为字段,
  • 为匿名类创建了一个构造函数,该构造函数接受对A 对象
  • ,稍后在 doAction() 方法中访问该对象。

(旁注:我必须将变量初始化为 new java.util.Random().nextInt() 以防止它优化掉大量代码。)


这里有类似的讨论

方法局部内部类访问方法的局部变量

Local variables are (obviously) not shared between different methods such as method() and doAction() above. But since it's final, nothing "bad" could happen in this case, so the language still allows it. The compiler however, needs to do something clever about the situation. Lets have a look at what javac produces:

$ javap -v "A\$1"           # A$1 is the anonymous Action-class.
...
final int val$i;    // A field to store the i-value in.

final A this$0;     // A reference to the "enclosing" A-object.

A$1(A, int);  // created constructor of the anonymous class
  Code:
   Stack=2, Locals=3, Args_size=3
   0: aload_0
   1: aload_1
   2: putfield #1; //Field this$0:LA;
   5: aload_0
   6: iload_2
   7: putfield #2; //Field val$i:I
   10: aload_0
   11: invokespecial #3; //Method java/lang/Object."<init>":()V
   14: return
   ...
public void doAction();
  Code:
   Stack=2, Locals=1, Args_size=1
   0: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: getfield #2; //Field val$i:I
   7: invokevirtual #5; //Method java/io/PrintStream.println:(I)V
   10: return

This actually shows that it

  • turned the i variable into a field,
  • created a constructor for the anonymous class, which accepted a reference to the A object
  • which it later accessed in the doAction() method.

(A side note: I had to initialize the variable to new java.util.Random().nextInt() to prevent it from optimizing away a lot of code.)


Similar discussion here

method local innerclasses accessing the local variables of the method

长安忆 2024-09-07 20:01:54

编译器会自动为您的匿名内部类生成一个构造函数,并将您的局部变量传递到该构造函数中。

构造函数将此值保存在类变量(字段)中,也名为 i,它将在“闭包”内部使用。

为什么必须是最终的?好吧,让我们探讨一下不存在的情况:

public class A {
    public void method() {
        int i = 0; // note: this is WRONG code

        doStuff(new Action() {
            public void doAction() {
                Console.printf(i);   // or whatever
            }
        });

        i = 4; // A
        // B
        i = 5; // C
    }
}

在情况 A 中,Action 的字段 i 也需要更改,让我们假设这是可能的:它需要引用到 Action 对象。

假设在情况 B 中,Action 的此实例已被垃圾收集。

现在情况 C:它需要一个 Action 实例来更新它的类变量,但该值被 GC 了。它需要“知道”它是 GCed,但这很困难。

因此,为了使VM的实现更简单,Java语言设计者表示它应该是final的,这样VM就不需要一种方法来检查对象是否消失,并保证变量不被修改,并且虚拟机或编译器不必保留匿名内部类及其实例中变量的所有用法的引用。

The compiler automatically generates a constructor for your anonymous inner-class, and passes your local variable into this constructor.

The constructor saves this value in a class variable (a field), also named i, which will be used inside the "closure".

Why it has to be final? Well let's explore the situation in where it isn't:

public class A {
    public void method() {
        int i = 0; // note: this is WRONG code

        doStuff(new Action() {
            public void doAction() {
                Console.printf(i);   // or whatever
            }
        });

        i = 4; // A
        // B
        i = 5; // C
    }
}

In situation A the field i of Action also needs to be changed, let's assume this is possible: it needs the reference to the Action object.

Assume that in situation B this instance of Action is Garbage-Collected.

Now in situation C: it needs an instance of Action to update it's class variable, but the value is GCed. It needs to "know" it's GCed, but that is difficult.

So to keep the implementation of the VM simpler, the Java language designers have said that it should be final such that the VM doesn't need a way to check whether an object is gone, and guarantee that the variable is not modified, and that the VM or compiler doesn't have to keep reference of all usages of the variable inside anonymous inner-classes and their instances.

-残月青衣踏尘吟 2024-09-07 20:01:54

本地类实例(匿名类)必须维护变量的单独副本,因为它可能比函数的寿命更长。为了避免同一作用域内两个同名的可修改变量发生混淆,该变量被强制为final。

有关更多详细信息,请参阅 Java Final - 一个持久的谜团

The local class instance (the anonymous class) must maintain a separate copy of the variable, as it may out-live the function. So as not to have the confusion of two modifiable variables with the same name in the same scope, the variable is forced to be final.

See Java Final - an enduring mystery for more details.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文