熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

為什麼匿名內部類參數必須為final類型

2013-11-23 19:07:06  來源: Java核心技術 
    在線文檔閱讀開發手記(一))  從程序設計語言的理論上局部內部類(即定義在方法中的內部類)由於本身就是在方法內部(可出現在形式參數定義處或者方法體處)因而訪問方法中的局部變量(形式參數或局部變量)是天經地義的是很自然的
   
    )  為什麼JAVA中要加上一條限制只能訪問final型的局部變量?
   
    )  JAVA語言的編譯程序的設計者當然全實現局部內部類能訪問方法中的所有的局部變量(因為從理論上這是很自然的要求)但是編譯技術是無法實現的或代價極高
   
    )  困難在何處?到底難在哪兒?
   
    局部變量的生命周期與局部內部類的對象的生命周期的不一致性!
   
    )  設方法f被調用從而在它的調用棧中生成了變量i此時產生了一個局部內部類對象inner_object它訪問了該局部變量i 當方法f()運行結束後局部變量i就已死亡了不存在了局部內部類對象inner_object還可能   一直存在(只能沒有人再引用該對象時它才會死亡)它不會隨著方法f()運行結束死亡這時出現了一個荒唐結果局部內部類對象inner_object要訪問一個已不存在的局部變量i!
   
    )  如何才能實現?當變量是final時通過將final局部變量復制一份復制品直接作為局部內部中的數據成員這樣當局部內部類訪問局部變量時其實真正訪問的是這個局部變量的復制品(即這個復制品就代表了那個局部變量)因此當運行棧中的真正的局部變量死亡時局部內部類對象仍可以訪問局部變量(其實訪問的是復制品給人的感覺好像是局部變量的生命期延長了
   
    那麼核心的問題是怎麼才能使得訪問復制品與訪問真正的原始的局部變量其語義效果是一樣的呢?
   
    當變量是final時若是基本數據類型由於其值不變因而其復制品與原始的量是一樣語義效果相同(若不是final就無法保證復制品與原始變量保持一致了因為在方法中改的是原始變量而局部內部類中改的是復制品)
   
    當變量是final時若是引用類型由於其引用值不變(即永遠指向同一個對象)因而其復制品與原始的引用變量一樣永遠指向同一個對象(由於是final從而保證只能指向這個對象再不能指向其它對象)達到局部內部類中訪問的復制品與方法代碼中訪問的原始對象永遠都是同一個即語義效果是一樣的否則當方法中改原始變量而局部內部類中改復制品時就無法保證復制品與原始變量保持一致了(因此它們原本就應該是同一個變量
   
    一句話這個規定是一種無可奈何也說明程序設計語言的設計是受到實現技術的限制的這就是一例 因為我就看到不少人都持這種觀點設計與想法是最重要的實現的技術是無關緊要的只要你作出設計與規定都能實現


   
    現在我們來看如果我要實現一個在一個方法中匿名調用ABSClass的例子
   
    public static void test(final String s){
   
    //或final String s = axman;
   
    ABSClass c = new ABSClass(){
   
    public void m(){
   
    int x = shashCode()
   
    Systemoutprintln(x)
   
    }
   
    };
   
    //其它代碼
   
    }
   
    從代碼上看在一個方法內部定義的內部類的方法訪問外部方法內局部變量或方法參數是非常自然的事但內部類編譯的時候如何獲取這個變量因為內部類除了它的生命周期是在方法內部其它的方面它就是一個普通類那麼它外面的那個局部變量或方法參數怎麼被內部類訪問?編譯器在實現時實際上是這樣的
   
    public static void test(final String s){
   
    //或final String s = axman;
   
    class OuterClass$ extends ABSClass{
   
    private final String s;
   
    public OuterClass$(String s){
   
    thiss = s;
   
    }
   
    public void m(){
   
    int x = shashCode()
   
    Systemoutprintln(x)
   
    }
   
    };
   
    ABSClass c = new OuterClass$(s)
   
    //其它代碼
   


    }
   
    即外部類的變量被作為構造方法的參數傳給了內部類的私有成員
   
    假如沒有final那麼
   
    public static void test(String s){
   
    //或String s = axman;
   
    ABSClass c = new ABSClass(){
   
    public void m(){
   
    s = other;
   
    }
   
    };
   
    Systemoutprintln(s)
   
    }
   
    就會編譯成
   
    public static void test(String s){
   
    //或String s = axman;
   
    class OuterClass$ extends ABSClass{
   
    private String s;
   
    public OuterClass$(String s){
   
    thiss = s;
   
    }
   
    public void m(){
   
    s = other;
   
    }
   
    };
   
    ABSClass c = new OuterClass$ (s)
   
    }
   
    內部類的s重新指向other並不影響test的參數或外部定義的那個s同理如果外部的s重新賦值內部類的s也不會跟著改變
   
    而你看到的
   
    public static void test(String s){
   
    //或String s = axman;
   
    ABSClass c = new ABSClass(){
   
    public void m(){
   
    s = other;
   
    }
   
    };
   
    Systemoutprintln(s)
   
    }
   
    在語法上是一個s在內部類中被改變了但結果打印的出來的你認為是同一的s卻還是原來的axman
   
    你能接收這樣的結果嗎?
   
    所以final從語法上約束了實際上兩個不同變量的一致性(表現為同一變量)


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26301.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.