爲何匿名內部類參數必須爲final類型

1)  從程序設計語言的理論上:局部內部類(即:定義在方法中的內部類),因爲自己就是在方法內部(可出如今形式參數定義處或者方法體處),於是訪問方法中的局部變量(形式參數或局部變量)是天經地義的.是很天然的java


2)  爲何JAVA中要加上一條限制:只能訪問final型的局部變量? this


3)  JAVA語言的編譯程序的設計者固然全實現:局部內部類能訪問方法中的全部的局部變量(由於:從理論上這是很天然的要求),可是:編譯技術是沒法實現的或代價極高. spa


4)  困難在何處?到底難在哪兒?
     局部變量的生命週期局部內部類的對象的生命週期的不一致性! 設計


5)  設方法f被調用,從而在它的調用棧中生成了變量i,此時產生了一個局部內部類對象inner_object,它訪問了該局部變量i .當方法f()運行結束後,局部變量i就已死亡了,不存在了.但:局部內部類對象inner_object還可能   一直存在(只能沒有人再引用該對象時,它纔會死亡),它不會隨着方法f()運行結束死亡.這時:出現了一個"荒唐"結果:局部內部類對象 inner_object要訪問一個已不存在的局部變量i! 對象


6)  如何才能實現?當變量是final時,經過將final局部變量"複製"一份,複製品直接做爲局部內部中的數據成員.這樣:當局部內部類訪問局部變量 時,其實真正訪問的是這個局部變量的"複製品"(即:這個複製品就表明了那個局部變量).所以:當運行棧中的真正的局部變量死亡時,局部內部類對象仍能夠 訪問局部變量(其實訪問的是"複製品"),給人的感受:好像是局部變量的"生命期"延長了. blog


那麼:核心的問題是:怎麼才能使得:訪問"複製品"與訪問真正的原始的局部變量,其語義效果是同樣的呢?
當變量是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 = s.hashCode(); 編譯器

 

      System.out.println(x);hash

   }
  };
  //其它代碼.
 } 編譯

 

 從代碼上看,在一個方法內部定義的內部類的方 法訪問外部方法內局部變量或方法參數,是很是天然的事,但內部類編譯的時候如何獲取這個變量,由於內部類除了它的生命週期是在方法內部,其它的方面它就是 一個普通類。那麼它外面的那個局部變量或方法參數怎麼被內部類訪問?編譯器在實現時其實是這樣的:


  public static void test(final String s){
     //或final String s = "axman";


  class OuterClass$1 extends ABSClass{ 

   private final String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
      int x = s.hashCode();

      System.out.println(x);

   }
  };

  ABSClass c = new OuterClass$1(s);
  //其它代碼.
 }


即外部類的變量被做爲構造方法的參數傳給了內部類的私有成員.
假如沒有final,那麼:
 public static void test(String s){
     //或String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
     s = "other";
   }
  };
  System.out.println(s);
 }
 就會編譯成:
  public static void test(String s){
     //或String s = "axman";

  class OuterClass$1 extends ABSClass{

 

   private String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
     s = "other";

   }
  };

   ABSClass c = new OuterClass$1 (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";
   }
  };
  System.out.println(s);
 }

 

 在語法上是一個s,在內部類中被改變了,但結果打印的出來的你認爲是同一的s卻仍是原來的"axman",
 你能接收這樣的結果嗎?
 因此final從語法上約束了實際上兩個不一樣變量的一致性(表現爲同一變量).

 

 

 

另外:java內部類能夠訪問外部類的private 變量,外部類也能夠訪問內部類的private

相關文章
相關標籤/搜索