你好!今天我們將了解一個重要的機制:嵌套類中的繼承。你有沒有想過如果你需要讓一個嵌套類繼承某個其他類你會怎麼做。如果不是,請相信我:這種情況可能會令人困惑,因為其中有很多細微差別。
  1. 我們是否讓嵌套類繼承某個類?或者我們讓一些類繼承一個嵌套類?
  2. 子/父類是普通的公共類,還是也是嵌套類?
  3. 最後,在所有這些情況下我們使用什麼類型的嵌套類?
所有這些問題都有如此多的可能答案,你的頭會旋轉:) 如你所知,我們可以通過將復雜的問題分解成更簡單的部分來解決它。讓我們這樣做吧。讓我們依次從兩個角度考慮每一組嵌套類:每一類嵌套類誰可以繼承,它又可以繼承給誰。讓我們從靜態嵌套類開始。

靜態嵌套類

嵌套類的繼承實例 - 2它們的繼承規則是最簡單的。在這裡,您幾乎可以做任何您想做的事。靜態嵌套類可以繼承:
  • 普通班級
  • 在外部類或其祖先中聲明的靜態嵌套類
回憶一下我們關於靜態嵌套類的課程中的示例。

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
       public static int getMaxPassengersCount() {
          
           return maxPassengersCount;
       }
   }
}
讓我們嘗試更改代碼並創建一個Drawing靜態嵌套類及其後代 — Boeing737Drawing

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }
  
   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
如您所見,沒問題。我們甚至可以把這個類拉出來Drawing,讓它成為一個普通的公共類,而不是靜態嵌套類——什麼都不會改變。

public class Drawing {
  
}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
我們明白這一點。但是什麼類可以繼承靜態嵌套類呢?幾乎任何!嵌套/非嵌套、靜態/非靜態——都沒有關係。這裡我們讓Boeing737Drawing內部類繼承Drawing靜態嵌套類:

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Boeing737Drawing您可以創建這樣的 實例:

public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
我們的類雖然Boeing737Drawing繼承了一個靜態類,但是它本身並不是靜態的!因此,它總是需要一個外部類的實例。我們可以把Boeing737Drawing類從Boeing737類中去掉,讓它成為一個簡單的公共類。沒有什麼改變。它仍然可以繼承Drawing靜態嵌套類。

public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;
   
}
唯一重要的一點是,在這種情況下,我們需要公開靜態maxPassengersCount變量。如果它保持私有,那麼普通的公共類將無法訪問它。我們找到了靜態類!:) 現在讓我們繼續討論內部類。它們有 3 種類型:簡單內部類、局部類和匿名內部類。 嵌套類的繼承實例 - 3同樣,讓我們從簡單到復雜:)

匿名內部類

匿名內部類不能繼承另一個類。沒有其他類可以繼承匿名類。再簡單不過了!:)

本地課程

本地類(以防你忘記了)在另一個類的代碼塊內聲明。大多數情況下,這發生在外部類的某些方法中。從邏輯上講,只有同一方法(或代碼塊)內的其他本地類才能繼承本地類。這是一個例子:

public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {
          
          
       }

       // ...number validation code
   }
}
這是我們關於本地課程的課程中的代碼。我們的數字驗證器類有一個PhoneNumber本地類。如果我們需要它代表兩個不同的實體,例如,一個手機號碼和一個固定電話號碼,我們只能在同一個方法中這樣做。原因很簡單:本地類的範圍僅限於聲明它的方法(代碼塊)。因此,我們將無法在外部使用它(包括用於類繼承)。然而,本地類本身繼承的可能性要大得多!本地類可以繼承:
  1. 一個普通的班級。
  2. 在與本地類相同的類或其祖先之一中聲明的內部類。
  3. 在同一方法(代碼塊)中聲明的另一個本地類。
第一點和第三點看起來很明顯,但第二點有點令人困惑:/讓我們看兩個例子。示例 1 —“使本地類繼承在與本地類相同的類中聲明的內部類”:

public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
這裡我們將PhoneNumber類從validatePhoneNumber()方法中移除,並使其成為內部類而不是本地類。這並不能阻止我們讓我們的 2 個本地類繼承它。示例 2 — “...或在此類的祖先中。” 現在這已經更有趣了。我們可以PhoneNumber在繼承鏈中移動到更高的位置。讓我們聲明一個抽像AbstractPhoneNumberValidator類,它將成為我們類的祖先PhoneNumberValidator

public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
如您所見,我們不只是聲明它——我們還將PhoneNumber內部類移入其中。但是,在它的後代中PhoneNumberValidator,在方法中聲明的本地類可以PhoneNumber毫無問題地繼承!

public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
由於繼承關係,後代類中的局部類“看到”祖先類中的內部類。最後,讓我們繼續最後一組 :)

內部類

在同一外部類(或其後代)中聲明的內部類可以繼承另一個內部類。讓我們使用內部類課程中的自行車示例來探索這一點。

public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }

   class SportSeat extends Seat {
      
       // ...methods
   }
}
這裡我們在類Seat內部聲明了內部類Bicycle。一種特殊類型的賽車座椅,SportSeat繼承了它。但是,我們可以創建一個單獨的“賽車”類型並將其放在一個單獨的類中:

public class SportBicycle extends Bicycle {
  
   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }

  
   class SportSeat extends Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
這也是一種選擇。後代的內部類(SportBicycle.SportSeat)“看到”了祖先的內部類並且可以繼承它們。繼承內部類有一個非常重要的特性!在前面兩個例子中,我們的SportSeat類是一個內部類。但是如果我們決定創建SportSeat一個同時繼承Seat內部類的普通公共類呢?

// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
我們得到一個錯誤!你能猜出為什麼嗎?:) 這一切都很簡單。我們在講Bicycle.Seat內部類的時候提到過,外部類實例的引用會隱式傳遞給內部類的構造函數。這意味著您不能Seat在不創建對象的情況下創建Bicycle對象。但是創建 a 呢SportSeat?與 不同Seat,它沒有這種內置機制來隱式地將對外部類實例的引用傳遞給構造函數。直到,沒有Bicycle對象,我們就無法創建SportSeat對象,就像 的情況一樣Seat。因此,我們只剩下一件事要做——顯式地將對象SportSeat的引用傳遞給構造函數Bicycle。方法如下:

class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
我們使用super(); Now 調用超類構造函數,如果我們想創建一個SportSeat對象,沒有什麼能阻止我們這樣做:

public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
呸!這節課相當長 :) 但是你學到了很多東西! 現在是時候解決一些任務了!:)