Kar*_*eem 1 java final field local-variables anonymous-class
我之前问过这个问题,但我得不到合适的答案.
如果非最终字段的值可以更改,那么如何在匿名类类中使用它们?
class Foo{
private int i;
void bar(){
i = 10
Runnable runnable = new Runnable (){
public void run (){
System.out.println(i); //works fine
}//end method run
}//end Runnable
}//end method bar
}//end class Foo
Run Code Online (Sandbox Code Playgroud)
如果在匿名类中使用的局部变量必须final使编译器能够在匿名类代码中内联它们的值,如下所示:
之前:
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}//end method run
};//end anonymous class
}//end method f
}//end class Access1
Run Code Online (Sandbox Code Playgroud)
后:
public class Access1 {
public Access1() {}//end constructor
public void f() {
Access1$1 access1$1 = new Access1$1(this);
}//end method f
}//end class Access1
Run Code Online (Sandbox Code Playgroud)
和
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}//end constructor
public void run() {
System.out.println(3);
}//end method run
private final Access1 this$0;
}//end class Access1$1
Run Code Online (Sandbox Code Playgroud)
那么编译器如何内联非最终字段的值?
方法调用的局部变量(必须是final内部类可访问的)和实例的私有数据成员之间存在很大差异.
内部类可以访问包含实例,也可以访问该实例的所有成员final.他们没有必要是最终的,因为他们被引用(在你的情况下)Foo.this.因此,当访问你的i成员时,内部类实际上是访问Foo.this.i,只是如果没有它的引用是明确的,那么Foo.this(如this)可以暗示.
但匿名类的代码不能以这种方式访问局部变量,因为它们(当然)不是包含类的实例成员.相反,编译器做了一件非常有趣的事情:它为每个局部变量创建一个匿名类的实例成员final,并且在创建匿名类的实例时,它使用局部变量的值初始化这些成员.
让我们看看它做到了:
public class InnerEx {
public static final void main(String[] args) {
new InnerEx().test("hi");
}
private void test(String arg) {
final String localVar = arg;
Runnable r = new Runnable() {
public void run() {
System.out.println(localVar);
}
};
r.run();
}
}
Run Code Online (Sandbox Code Playgroud)
编译时,我们得到InnerEx.class和InnerEx$1.class.如果我们反编译InnerEx$1.class,我们会看到:
class InnerEx$1 implements java.lang.Runnable {
final java.lang.String val$localVar;
final InnerEx this$0;
InnerEx$1(InnerEx, java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LInnerEx;
5: aload_0
6: aload_2
7: putfield #2 // Field val$localVar:Ljava/lang/String;
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void run();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field val$localVar:Ljava/lang/String;
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
}
Run Code Online (Sandbox Code Playgroud)
请注意调用的实例成员val$localVar,它是为调用中的本地变量而创建的实例成员InnerEx#test.
| 归档时间: |
|
| 查看次数: |
787 次 |
| 最近记录: |