1. 程式人生 > 實用技巧 >Java 面向物件:內部類

Java 面向物件:內部類

禁止碼迷,布布扣,豌豆代理,碼農教程,愛碼網等第三方爬蟲網站爬取!
目錄

內部類

內部類是定義在另一個類中的類,例如 TalkingClock 類中內嵌了 TimePrinter 類:

class TalkingClock{
   private int interval;
   private boolean beep;

   public TalkingClock(int interval, boolean beep){}
   public void start(){}

   public class TimePrinter implements ActionListener{
      public void actionPerformed(ActionEvent event){}
   }
}

內部類可以對同一個包中的其他類隱藏細節,同時內部類中的方法可以訪問原來的類中的作用域資料。

訪問外部物件

內部類可以訪問自身的資料欄位,也可以訪問建立它的外圍類物件的資料欄位。內部類會有一個隱式引用,指向建立它的外部類物件。

對外圍類的引用在構造器中設定,編譯器會修改所有內部類的構造器,新增一個對外部類引用的引數。使用外部類引用的語法為:

OuterClass.this

例如內部類 TimePrinter 引用外部類 TalkingClock 的 beep 引數,嚴格來說要寫成這樣。

TalkingClock.this.beep

訪問內部物件

使用內部類的構造器時,可以使用下面的語法來實現:

outerObject.new InnerClass(construction parameters)

在外部類的作用域之外,想要引用內部類就需要用下面的語法:

OuterClass.InnerClass

區域性內部類

區域性內部類類似方法的區域性變數,在類外或者類的其他方法中不能訪問這個內部類。通常情況下可以在某個方法中區域性實現一個類,這個類將用於輔助該方法的功能實現。例如:

public void start(){
      class TimePrinter implements ActionListener{
            public void actionPerformed(ActionEvent event){
                  System.out.println("The time is " + Instant.ofEpochMilli(event.getWhen()));
                  if (beep) {
                        Toolkit.getDefaultToolkit().beep();
                  }
            }
      }

      TimePrinter listener = new TimePrinter();
      TalkingClock timer = new Timer(interval, listener);
      timer.start();
} 

宣告區域性類時不能有訪問說明符,區域性類的作用域被限定在了這個區域性類塊中。區域性類可以做到對外界的絕對隱藏,這是個很大的優勢。
區域性類還有個優點,就是可以訪問區域性變數。當局部變數傳參進來時,區域性變數也會被傳入構造器,此時編譯器會為區域性變數提供相應的例項欄位,儲存一個副本。這麼做的好處是,當函式呼叫結束導致區域性變數消失時,區域性類還能夠使用這個區域性變數。例如將上述的 start 方法改為傳入 2 個引數,仍然不影響正常使用。

public void start(int interval, boolean beep)

匿名內部類

使用區域性類時,如果只是想建立這個區域性類的一個物件,甚至不需要給出這個類的名字,這種類就成為匿名內部類。匿名內部類的語法為:

new SuperType(construction parameters){
      inner class methods and data
}

new InterfaceType(){
      methods and data
}

SuperType 指的是超類,此時區域性類就需要拓展這個類。匿名區域性類可以基於介面建立,此時區域性類就要實現這個介面。由於構造器的名字必須和類名相同,而匿名區域性類沒有類名,因此匿名區域性類不能有構造器,此時如果要構造引數就需要傳遞給超類的構造器。例如將上述的區域性類改造成匿名內部類:

public void start(){
      var listener = new ActionListener(){
               public void actionPerformed(ActionEvent event){
                  System.out.println("The time is " + Instant.ofEpochMilli(event.getWhen()));
                  if (beep) {
                        Toolkit.getDefaultToolkit().beep();
                  }
               } 
          }
      TalkingClock timer = new Timer(interval, listener);
      timer.start();
} 

靜態內部類

有時候使用內部類只是想隱藏一個類,並不需要內部類有外部類的引用,因此可以用 static 關鍵字宣告靜態內部類。例如定義 ArrayAlg 類中實現 minmax 方法,minmax 方法的功能是同時返回一個數組的最大值和最小值,但是用 return 只能返回一個值。因此可以定義一個靜態內部類 Pair,Pair 類有兩個引數可以分別儲存最大和最小值,這樣就可以通過返回 Pair 方法同時返回 2 個值了。

class ArrayAlg{
   public static class Pair{
      private double first;
      private double second;

      public Pair(double f, double s){
         first = f;
         second = s;
      }

      public double getFirst() { return first; }
      public double getSecond() { return second; }
   }

   public static Pair minmax(double[] values){
      double min = Double.POSITIVE_INFINITY;
      double max = Double.NEGATIVE_INFINITY;
      for (double v : values){
         if (min > v) min = v;
         if (max < v) max = v;
      }
      return new Pair(min, max);
   }
}

只要內部類不需要訪問外圍類物件,就應該宣告為靜態內部類。和其他內部類不同,靜態內部類可以有靜態欄位和方法。

參考資料

《Java 核心技術 卷Ⅰ》,[美]Cay S.Horstmann 著,林琪 蘇鈺涵 等譯,機械工業出版社