CodeGym /Java блог /Случаен /Примери за наследяване на вложени класове
John Squirrels
Ниво
San Francisco

Примери за наследяване на вложени класове

Публикувано в групата
здрасти Днес ще разгледаме важен механизъм: наследяване във вложени класове. Замисляли ли сте се Howво бихте направor, ако трябва да накарате вложен клас да наследи друг клас. Ако не, повярвайте ми: тази ситуация може да бъде объркваща, защото има много нюанси.
  1. Караме ли вложен клас да наследи няHowъв клас? Или караме някой клас да наследява вложен клас?
  2. Класът дете/родител обикновен публичен клас ли е or също е вложен клас?
  3. И накрая, Howъв тип вложени класове използваме във всички тези ситуации?
Има толкова много възможни отговори на всички тези въпроси, че главата ви ще се завърти :) Както знаете, можем да решим сложен проблем, като го разделим на по-прости части. Нека го направим. Нека разгледаме всяка група от вложени класове на свой ред от две гледни точки: кой може да наследи всеки тип вложен клас и кого може да наследи. Нека започнем със статични вложени класове.

Статични вложени класове

Примери за наследяване на вложени класове - 2Техните правила за наследяване са най-прости. Тук можете да правите почти всичко, което сърцето ви желае. Статичен вложен клас може да наследява:
  • обикновен клас
  • статичен вложен клас, който е деклариран във външен клас or неговите предци
Спомнете си пример от нашия урок за статични вложени класове.

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;
       }
   }
}
Нека се опитаме да променим codeа и да създадем 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класа и да го направим обикновен публичен клас instead of статичен вложен клас — нищо няма да се промени.

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;
       }
   }
}
Ние разбираме това. Но кои класове могат да наследят статичен вложен клас? На практика всяHowви! Вложени/невложени, статични/нестатични — няма meaning. Тук караме 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Отново, нека преминем от просто към сложно :)

Анонимни вътрешни класове

Анонимен вътрешен клас не може да наследи друг клас. Никой друг клас не може да наследи анонимен клас. Не може да бъде по-просто! :)

Местни класове

Локалните класове (в случай, че сте забравor) се декларират в codeов блок на друг клас. Най-често това се случва вътре в някой метод от външния клас. Логично, само други локални класове в същия метод (or codeов блок) могат да наследят локален клас. Ето един пример:

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
   }
}
Това е codeът от нашия урок за локални класове. Нашият клас валидатор на номера има PhoneNumberлокален клас. Ако имаме нужда от него, за да представи две отделни единици, например мобилен телефонен номер и стационарен телефонен номер, можем да направим това само в рамките на един и същи метод. Причината е проста: обхватът на локалния клас е ограничен до метода (codeовия блок), където е деклариран. В резултат на това няма да можем да го използваме външно (включително за наследяване на класове). Възможностите за наследяване в самия локален клас обаче са много по-широки! Локален клас може да наследи:
  1. Обикновен клас.
  2. Вътрешен клас, който е деклариран в същия клас като локалния клас or в един от неговите предци.
  3. Друг локален клас, деклариран в същия метод (codeов блок).
Първата и третата точка изглеждат очевидни, но втората е малко объркваща :/ Нека да разгледаме два примера. Пример 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()метода и го направихме вътрешен клас instead of локален клас. Това не ни пречи да накараме нашите 2 локални класа да го наследят. Пример 2 — "... or в предците на този клас." Сега това вече е по-интересно. Можем да се издигнем 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без ниHowъв проблем!

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
   }
}
Поради връзката на наследяване, локалните класове вътре в клас потомък "виждат" вътрешните класове вътре в предшественик. И накрая да преминем към последната група :)

Вътрешни класове

Вътрешен клас, деклариран в същия външен клас (or в негов потомък), може да наследи друг вътрешен клас. Нека проучим това, използвайки нашия пример с велосипеди от урока за вътрешните класове.

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клас беше вътрешен клас. Но Howво ще стане, ако решим да направим 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обект. Но Howво да кажем за създаването на SportSeat? За разлика от Seat, той няма този вграден механизъм за имплицитно предаване на конструктора на препратка към екземпляр на външния клас. И все пак, без Bicycleобект, не можем да създадем SportSeatобект, точно Howто в случая с Seat. Следователно, остава само едно нещо, което трябва да направим — изрично да предадем на SportSeatконструктора препратка към Bicycleобект. Ето How да го направите:

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);

   }
}
уф! Този урок беше доста дълъг :) Но научихте много! Сега е време да решите някои задачи! :)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION