2009/03/12

为何匿名内部类访问外部类方法中的局部变量,这个变量必须是final型
public class Test {

    /**
     * @param args
     */
    public static void main(String[] args){
        Test t= new Test();
        System.out.println(t.getA().getO());
        System.gc();
        System.out.println(t.getA().getO());
    }
    public A getA(){
        final Object o = new Object();
        
        return new A(){

            @Override
            public Object getO() {
                
                return o;
            }
            
        };
    }
}
interface A{
    public Object getO();
}
看上面一段代码。
为何下划线的那段代码要用红色的final呢?
其实大家有没有想过,如果不用final会发生什么事情?首先,这个对象会随时被垃圾回收器收集掉(看起来是这样,呵呵,实际上不是这样),这就失去了稳定性.
其次,这段代码在编译的时候,外部类Test是作为一个指针送入匿名内部类中的,但这对于取得常引用o指向的对象没有意义,因为那个final型引用是方法体内部的局部引用,那这个匿名内部类的类体内部在编译时就无法获得一个局部.
实际上,从java汇编指令可以看出来,实际的操作是在匿名内部类中形成了一个类似私有的final型引用,而那个局部变量的引用将被复制一份存在于匿名内部类中,这个引用指向这个外部类方法中的常引用对象.实际上,java完全可以做到非final型的局部引用被内部类引用,这样就会使代码的可读性变差,或者说是可理解性变差.
假设下面代码能通过编译,
 public A[] getAArray(){
       /* final*/ Object o = new Object(){
                    public String toString(){return "Obj";}
            };//
        
        A a1= new A(){

            @Override
            public Object getO() {
                return o;
            }
        };
        A a2=new A(){
            @Override
            public Object getO() {
                o=null;
                return o;
            }
        }
        A[] as=new A[2]{a1,a2};
        return as;
    }


as[1].getO();//null
as[0].getO();//"Obj"
但是从字面上理解,as[1].getO();as[0].getO();操作的都是"/* final*/ Object o",但事实上,按照java的实现机制不是操作的同一个引用,这就引起了歧义.
我说的可能有点拗口,大家可以仔细去理解一下这里面的关系.



2 评论 :

goooooood girl 说...

your blog is feel good......

恶魔的尾巴 说...

thanks.but could you tell what is good?