java如何实现内部类闭包?
在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
局部变量(显然)不在不同方法之间共享,例如上面的
method()
和doAction()
。但由于它是最终的,在这种情况下不会发生任何“坏”事情,因此语言仍然允许它。然而,编译器需要对这种情况采取一些巧妙的措施。让我们看看 javac 产生了什么:这实际上表明它将
A
对象doAction()
方法中访问该对象。(旁注:我必须将变量初始化为
new java.util.Random().nextInt()
以防止它优化掉大量代码。)这里有类似的讨论
方法局部内部类访问方法的局部变量
Local variables are (obviously) not shared between different methods such as
method()
anddoAction()
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 whatjavac
produces:This actually shows that it
i
variable into a field,A
objectdoAction()
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
编译器会自动为您的匿名内部类生成一个构造函数,并将您的局部变量传递到该构造函数中。
构造函数将此值保存在类变量(字段)中,也名为
i
,它将在“闭包”内部使用。为什么必须是最终的?好吧,让我们探讨一下不存在的情况:
在情况 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:
In situation A the field
i
ofAction
also needs to be changed, let's assume this is possible: it needs the reference to theAction
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.
本地类实例(匿名类)必须维护变量的单独副本,因为它可能比函数的寿命更长。为了避免同一作用域内两个同名的可修改变量发生混淆,该变量被强制为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.