我之前本来在网上看过为什么java匿名内部类的参数为final类型的这方面介绍的,但是昨天写程序的时候又看到这个,突然想不起来为什么了。然后我又查了一下,但是找不到我之前看的那篇说的很详细的文章了,网上说的有点似懂非懂,后面自己推理了下才想明白。
一般我们像在test.addListener(new Listener)创建这样的匿名内部类
public class Test1 {
Listener mListener;
interface Listener{
void onListener(Object b);
}
void useListener() {
final String a = new String();
Test1 test = new Test1();
test.addListener(new Listener() {
public void onListener(Object b) {
System.out.println(a);
}
});
}
void addListener(Listener listener){
this.mListener = listener;
}
}
编译器回把这个匿名内部类单独编译成一个类
class Test1$1
implements Test1.Listener
{
private final Test1 val$0;
private final String val$a;
Test1$1(Test1 paramTest1, String paramString)
{
this.val$0 = paramTest1;
this.val$a = paramString;
}
public void onListener(Object paramObject)
{
System.out.println(this.val$a);
}
}
它会把外部类和内部类里用到的参数通过构造函数传到内部类里,可能这是因为方法体内的参数在方法执行完后,如果没有其它地方引用的话可能就被回收掉了,所以内部类创建一个参数继续引用。
那为什么一定要是final类型呢?
假如a刚传到内部类时
这时创建了一个val$a来指向a的引用,如果a不是final类型的,假如在方法后面a又指向了另一个object
这时就造成了内部类和方法体的参数不一致,因为本质上我们在编写内部类的时候是看不到val$a这个编译器自动创建的参数,我们在内部类中还是使用a(编译器把它转换成了val$a),还是希望使用的是a指向的那个实体,而不是val$a指向的以前那个实体。
而内部类回调有不确定性,有可能在你给a重新赋值之前也可能在之后。到时可能出现两次运行不一样的结果,所以就干脆把参数设定成final不让它进行改变,这样就可以避免很多麻烦了。全局变量就不需要final了,因为内部类默认有一个外类的引用,可以直接通过外部类来使用全局变量,不需要通过构造函数传递。
这是在网上看到的一些资料,然后进行的一点推理,如果有不对的地方,欢迎大家指正。