the*_*oop 24 java closures inner-classes
在Java中,匿名内部类可以引用其本地范围中的变量:
public class A {
public void method() {
final int i = 0;
doStuff(new Action() {
public void doAction() {
Console.printf(i); // or whatever
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是这是如何实际实现的?如何i进入匿名内部doAction实现,为什么必须这样做final?
aio*_*obe 16
局部变量(显然)不不同的方法,例如之间共享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
Run Code Online (Sandbox Code Playgroud)
这实际上表明了它
i变量变成了一个字段,A对象的引用doAction()方法中访问.(旁注:我必须初始化变量new java.util.Random().nextInt()以防止它优化掉大量代码.)
这里类似的讨论
Pin*_*juh 12
编译器自动为您的匿名内部类生成构造函数,并将您的局部变量传递给此构造函数.
构造函数将此值保存在类变量(字段)中,该类变量也称为"变量" 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
}
}
Run Code Online (Sandbox Code Playgroud)
在情况A中i,Action还需要更改字段,让我们假设这是可能的:它需要对Action对象的引用.
假设在情况B中,这个实例Action是Garbage-Collected.
现在处于情境C:它需要一个实例Action来更新它的类变量,但值是GCed.它需要"知道"它的GCed,但这很难.
因此,为了保持虚拟机更简单的实现,Java语言的设计者说,这应该是最终使得虚拟机并不需要一种方法来检查对象是否已经过去,保证变量不被修改,并且VM或编译器不必在匿名内部类及其实例中保留变量的所有用法的引用.
| 归档时间: |
|
| 查看次数: |
4934 次 |
| 最近记录: |