やあ!今日は、入れ子になったクラスの継承という重要なメカニズムを見ていきます。ネストされたクラスに他のクラスを継承させる必要がある場合にどうするかを考えたことはありますか。そうでない場合は、信じてください。この状況には多くのニュアンスがあるため、混乱を招く可能性があります。
  1. ネストされたクラスに何らかのクラスを継承させているのでしょうか?それとも、一部のクラスにネストされたクラスを継承させているのでしょうか?
  2. 子/親クラスは通常のパブリック クラスですか、それともネストされたクラスでもありますか?
  3. 最後に、これらすべての状況でどのようなタイプのネストされたクラスを使用すればよいでしょうか?
これらすべての質問には答えがたくさんあるので、頭がクラクラしてしまいます :) ご存知のとおり、複雑な問題は、より単純な部分に分割することで解決できます。そうしよう。入れ子になったクラスの各グループを、各種類の入れ子になったクラスを誰が継承できるか、および誰が継承できるかという 2 つの観点から順番に検討してみましょう。静的ネストされたクラスから始めましょう。

静的入れ子クラス

ネストされたクラスの継承の例 - 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ローカル クラスがあります。携帯電話番号と固定電話番号など、2 つの異なるエンティティを表す必要がある場合、これを同じメソッド内でのみ行うことができます。理由は簡単です。ローカル クラスのスコープは、それが宣言されているメソッド (コード ブロック) に限定されます。その結果、外部から (クラスの継承を含む) 使用することができなくなります。ただし、ローカル クラス自体内での継承の可能性はさらに広いです。ローカル クラスは以下を継承できます。
  1. 普通のクラス。
  2. ローカル クラスと同じクラス、またはその祖先の 1 つで宣言された内部クラス。
  3. 同じメソッド (コード ブロック) で宣言された別のローカル クラス。
最初と 3 番目の点は明白に見えますが、2 番目の点は少しわかりにくいです :/ 2 つの例を見てみましょう。例 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) は、祖先の内部クラスを「認識」し、それらを継承できます。内部クラスの継承には、非常に重要な機能が 1 つあります。前の 2 つの例では、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。しかし、 の作成についてはどうでしょうかSportSeat。とは異なりSeat、コンストラクターに外部クラスのインスタンスへの参照を暗黙的に渡すための組み込みメカニズムがありません。オブジェクトがなければ、の場合と同様に、オブジェクトをBicycle作成することはできません。したがって、私たちが行うべきことは 1 つだけ残っています。それは、オブジェクトへの参照をコンストラクターに明示的に渡すことです。その方法は次のとおりです。 SportSeatSeatSportSeatBicycle

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(); さて、オブジェクトを作成したい場合は、次のコマンド を使用してスーパークラス コンストラクターを呼び出しますSportSeat

public class Main {

   public static void main(String[] args) {

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

   }
}
ふう!このレッスンはかなり長かったです :) でも、たくさんのことを学びました! 今度はいくつかのタスクを解決します。:)