1. 程式人生 > >匿名內部類和lambda表示式

匿名內部類和lambda表示式

以下內容為筆者在學習過程中對相關知識點的理解和實踐的記錄,如有謬誤,還請指出。

一、匿名內部類:我看到很多人有提到“匿名內部類是沒有名字的類”,個人覺得用陳國君主編的《Java程式設計基礎(第五版)》中的 “所謂匿名內部類,是指可以利用內部類建立沒有名稱的物件,它一步完成了宣告內部類和建立該類的一個物件,並利用該物件訪問到類裡面的成員”來描述,或許要更好理解一些。

下面,我們來看一段程式碼:

package lambdatest;
public class InnerclassTest {  //外部類
    String name="黃林";
  public static void main(String []args){
      (
              new InnerclassTest(){  //匿名內部類開始
                  void setName(String n){
                      this.name = n;
                      System.out.println("內部類---姓名:"+name);
                  }
              }
              ).setName("王佳");      //匿名內部類結束
  }
  
}

這是它的執行結果:

這裡的內部類是直接用外部類InnerclassTest的名字new一個物件,並緊接著定義類體的,這個內部類沒有自己的名字,甚至將建立物件的工作直接交給了其父類InnerclassTest(筆者認為:匿名內部類繼承自外部類,或者實現外部類介面),在匿名內部類中重新給其父類成員name賦值,所以最後的結果是匿名內部類中所給的值將其父類成員的初始值給覆蓋了。

匿名內部類不需要class關鍵字,自然也不需要public,private等修飾,它沒有構造方法,一般用來新增內部類中缺少的內容,或者實現外部介面或其抽象父類中的抽象方法。比如上面的程式碼就為其外部類中的成員name屬性添加了setName()方法,並利用匿名內部類物件呼叫該方法併為name賦值。

package lambdatest;
public class InnerclassTest {
    String name="黃嵐嵐";
    void test(){
        System.out.println("這是外部類中的方法~");
    }
  public static void main(String []args){

      (
              new InnerclassTest(){
                  void setName(String n){
                      this.name = n;
                      System.out.println("內部類---姓名:"+name);
                  }
                  void test(){
                      System.out.println("這是內部類中Test方法的輸出結果,看看我會不會覆蓋外部類中的方法~"+name);
                  }
              }
//              ).setName("王佳");
      ).test();
  }

}

這是它的執行結果:

從執行結果來看,匿名內部類中的test()方法對其父類(外部類進行了覆蓋),而name仍然是初值,沒有改變。

我們再看下面這段程式碼:

package lambdatest;
public class InnerclassTest {  //這是一個包含內部類的外部類
  public static void main(String []args){
      (
              new Inner(){  //匿名內部類開始
                  void setName(String n){
                      this.name = n;
                      System.out.println("內部類---姓名:"+name);
                  }
              }
              ).setName("王佳"); //匿名內部類結束
  }

  static class Inner{  //這是一個普通的內部類,,,mian方法是static的,這裡不加static會出錯,報錯資訊見下圖
      String name="黃嵐嵐";
    }
}

在上面的程式碼中,包含了一個內部類Inner和用Inner來建立的匿名內部類,其中,Inner是InnerclassTest的一個成員內部類。

內部類:一個類定義在另一個類的內部。其中,內部類又可以分為成員內部類(定義在外部類中,與其它外部類中的屬性、方法同級)和區域性內部類(定義在外部類的方法裡面)

package lambdatest;
public class InnerclassTest {
    public static void main(String []args) {
        Inner i = new Inner() {
            //  static int aa;             //匿名內部類中不允許出現靜態屬性
        void test () {
            System.out.println("這是匿名內部類,執行看會發生什麼");
        }
        void haha(){   //如果父類中沒有定義該方法,就不能通過父類物件來訪問子類特有的方法
            System.out.println("hahahahahahahhahahahahah~");
        }

//        static void hi(){    //匿名內部類中不允許出現靜態方法
//
//        }

    };
        i.test();
        System.out.println(i.name);
//        ((Inner) i).haha();
    }

    static class Inner{  //mian方法是static的,這裡不加static會出錯
        String name="黃嵐嵐";
        void test(){
            System.out.println("這是內部類");
        }
//        void haha(){
//            System.out.println("hahahahahahahhahahahahah~");
//        }
    }
}

在上面的程式碼中,同樣包含了一個內部類Inner和用Inner來建立的匿名內部類,其中,Inner是InnerclassTest的一個成員內部類。

這裡的寫法跟上面的不太一樣,但仔細看一下,其實是用成員內部類Inner例項化了一個物件 i ,在這裡,Inner就變成了那個利用Inner來建立的匿名內部類的父類。

通過父類物件訪問子類的成員時,只限於“覆蓋”的情況發生時。即子類中的成員test()對父類中同名且引數個數相同(都是無參)的test()進行了覆蓋;如果釋放 ((Inner) i).haha(); 則會出現錯誤,因為其父類Inner中並沒有定義 haha()方法,所以不能通過其父類物件i去訪問子類中特有的方法。

在這裡,因為i是父類Inner的例項化物件,所以用i訪問不到匿名內部類中的haha()方法。再次強調:i時Inner的物件,不是匿名內部類的,訪問不了匿名內部類的特有方法,只能覆蓋。

接著我們看看用匿名內部類實現介面的情況:

package lambdatest;
public class InnerclassTest {  //外部類
    
  public static void main(String []args){

      (
              new Inner(){  //匿名內部類開始
                  public void test(){  //不用public修飾不通過,修飾範圍>=接口才能覆蓋
                      System.out.println("這是外部類介面中Test方法的實現類~"+name);
                  }
              }
      ).test(); //匿名內部類結束
  }

   interface Inner{  //定義一個介面
       String name="黃嵐嵐";   // 預設public static final  ,值只能賦一次,且必須賦初值
      void test(); //預設用 public abstract修飾
    }
}

在上面的程式碼中,在InnerclassTest中定義了一個特殊的內部類------Inner介面(介面是一個特殊的抽象類),裡面有一個屬性name和一個抽象方法test();同樣,用Inner介面new了一個匿名內部類,並實現了它裡面的抽象方法test();

讀到這裡,相信你們也都看出來了,內部類繼承一個類不需要extends修飾,實現一個介面也不需要implements修飾,因為匿名內部類本身沒有自己的名字,它的定義與建立該類的例項是同時進行的,也就是說它的前面用new運算子,而不是class關鍵字,用其父類名或父介面名加上一個"()"就完成了匿名物件的建立。

建立匿名內部類並訪問其成員的語法格式為:

(

new 類名或介面名()   // ()中不能有引數

{

方法名(引數1,引數2,……)

{

//方法體

}

}

).方法名(引數1,引數2,……);