JavaRush /Java Blog /Random-IT /Ereditarietà delle classi nidificate

Ereditarietà delle classi nidificate

Pubblicato nel gruppo Random-IT
Ciao! Oggi esamineremo il funzionamento di un meccanismo importante: l'ereditarietà nelle classi nidificate. Non so se hai mai pensato a cosa farai quando avrai bisogno di ereditare una classe nidificata da qualcun'altra. In caso contrario, credimi: questa situazione può creare confusione, perché ci sono molte sfumature qui:
  1. Ereditiamo una classe nidificata da qualche classe o ereditiamo un'altra classe da una classe nidificata?
  2. Il successore/ereditato è una classe pubblica regolare o è anche una classe annidata?
  3. Infine, che tipo esatto di classi nidificate stiamo utilizzando in tutte queste situazioni?
Se rispondi a tutte queste domande, ci saranno così tante risposte possibili che ti girerà la testa :) Come sai, per risolvere un problema complesso è necessario dividerlo in parti più semplici. Questo è quello che faremo. Esaminiamo a turno ciascun gruppo di classi nidificate da due prospettive: chi può ereditare da questo tipo di classe nidificata e da chi può ereditare. Cominciamo con le classi nidificate statiche.

Classi nidificate statiche

Esempi di ereditarietà di classi interne - 2Le loro regole di eredità sono le più semplici. Qui puoi fare quasi tutto ciò che il tuo cuore desidera. Una classe nidificata statica può essere ereditata da:
  • lezione regolare
  • una classe nidificata statica dichiarata nella classe esterna o nei suoi antenati
Ricordiamo l'esempio della lezione sulle classi nidificate statiche.
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;
       }
   }
}
Proviamo a modificare il codice e creare una classe nidificata statica Drawinge il suo discendente - 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;
       }
   }
}
Come puoi vedere, nessun problema. Possiamo rimuovere del tutto la classe Drawinge renderla una normale classe pubblica invece di una classe nidificata statica: non cambierà nulla.
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;
       }
   }
}
Questo è tutto risolto. E quali classi possono ereditare da una nidificata statica? Quasi tutti! Annidato/regolare, statico/non statico: non importa. Qui ereditiamo la classe interna Boeing737Drawingda quella statica annidata 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;
       }
   }
}
Puoi creare un'istanza Boeing737Drawingcome questa:
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());

   }

}
Sebbene la nostra classe Boeing737Drawingerediti da una classe statica, essa stessa non è statica! Pertanto avrà sempre bisogno di un'istanza della classe esterna. Possiamo eliminare la classe Boeing737Drawingdalla classe Boeing737e renderla solo una classe pubblica. Non cambierà nulla: può anche ereditare da un file .nidificato statico 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;

}
Unico punto importante: in questo caso dobbiamo rendere maxPassengersCountpubblica la variabile statica. Se rimane privato, la normale classe pubblica non avrà accesso ad esso. Abbiamo risolto le lezioni statiche! :) Ora passiamo alle classi interne. Come ricorderete, ne esistono 3 tipi: classi interne semplici, classi locali e classi interne anonime. Esempi di ereditarietà di classi interne - 3Ancora una volta, passiamo dal semplice al complesso :)

Classi interne anonime

Una classe interna anonima non può ereditare da un'altra classe. Nessun'altra classe può ereditare da una classe anonima. Non potrebbe essere più semplice! :)

Classi locali

Le classi locali (nel caso te ne fossi dimenticato) sono dichiarate all'interno di un blocco di codice di un'altra classe. Molto spesso - all'interno di qualche metodo di questa classe esterna. È logico che solo altre classi locali all'interno dello stesso metodo (o blocco) possano ereditare da una classe locale. Ecco un esempio:
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 {


       }

       //...code валидации номера
   }
}
Questo è il codice della nostra lezione sulle lezioni locali. All'interno della classe di validazione del numero abbiamo una classe locale PhoneNumber: il numero di telefono. Se per i nostri scopi dobbiamo estrarre da esso due entità separate, ad esempio un numero di cellulare e un numero di telefono fisso, possiamo farlo solo con lo stesso metodo. Il motivo è semplice: lo scope di una classe locale è all'interno del metodo (blocco) in cui è dichiarata. Pertanto, non saremo in grado di utilizzarlo in qualche modo esternamente (anche per ereditarietà). Tuttavia, la stessa classe locale ha possibilità più ampie di eredità! Una classe locale può ereditare da:
  1. Lezione regolare.
  2. Una classe interna dichiarata nella stessa classe della classe locale o nei suoi antenati.
  3. Da un'altra classe locale dichiarata nello stesso metodo (blocco).
Il primo e il terzo punto sembrano ovvi, ma il secondo crea un po' di confusione :/ Vediamo due esempi. Esempio 1 - "ereditare una classe locale da una classe interna dichiarata nella stessa classe della classe locale":
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);
           }
       }

       //...code валидации номера
   }
}
Qui abbiamo eliminato la classe PhoneNumberdal metodo validatePhoneNumber()e l'abbiamo resa interna invece che locale. Ciò non ci impedisce di ereditare da esso le nostre 2 classi locali. Esempio 2 – “…o negli antenati di questa classe”. È qui che diventa più interessante. Possiamo portarlo PhoneNumberancora più in alto nella catena ereditaria. Dichiariamo una classe astratta AbstractPhoneNumberValidatorche diventerà l'antenata della nostra 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;
       }
   }

}
Come puoi vedere, non solo l'abbiamo dichiarato, ma vi abbiamo anche spostato la classe interna PhoneNumber. Tuttavia, nella sua classe discendente, PhoneNumberValidatorle classi locali nei metodi possono ereditare da 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);
           }
       }

       //...code валидации номера
   }
}
Grazie alla connessione tramite ereditarietà, le classi locali all'interno di una classe discendente “vedono” le classi interne all'interno dell'antenato. E infine, passiamo all'ultimo gruppo :)

Classi interiori

Una classe interna può essere ereditata da un'altra classe interna dichiarata nella stessa classe esterna (o dal suo discendente). Consideriamolo utilizzando l'esempio della bicicletta tratto dalla lezione sulle classi interne.
public class Bicycle {

   private String model;
   private int mawWeight;

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

   public void start() {
       System.out.println("Go!");
   }

   class Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }

   class SportSeat extends Seat {

       //...methods
   }
}
BicycleQui abbiamo dichiarato una classe interna alla classe Seat- posto. Da esso è stato ereditato un sottotipo speciale di sedili da corsa: SportSeat. Tuttavia, potremmo creare un tipo separato di "bici da corsa" e inserirlo in una classe separata:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int mawWeight) {
       super(model, mawWeight);
   }


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }
}
Anche questo è possibile. La classe interiore del bambino ( SportBicycle.SportSeat) “vede” le classi interiori dell'antenato e può ereditare da esse. L'ereditarietà dalle classi interne ha una caratteristica molto importante! Nei due esempi precedenti avevamo SportSeatinternal. Ma cosa succede se decidiamo di renderla SportSeatuna classe pubblica regolare, che eredita anche dalla classe interna Seat?
//ошибка! No inclosing instance of  type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
Abbiamo ricevuto un errore! Riuscite a indovinare a cosa è collegato? :) È semplice. Quando abbiamo parlato della classe interna Bicycle.Seat, abbiamo detto che il costruttore della classe interna passa implicitamente un riferimento a un oggetto della classe esterna. Pertanto, senza creare un oggetto, Bicyclenon è possibile creare un oggetto Seat. E la creazione SportSeat? Non ha lo stesso meccanismo integrato per passare implicitamente un riferimento a un oggetto di classe esterna nel costruttore come in Seat. Tuttavia, senza un oggetto Bicycle, proprio come nel caso di Seat, non possiamo creare un oggetto SportSeat. Pertanto, ci resta solo una cosa da fare: passare esplicitamente SportSeatun riferimento all'oggetto al costruttore. BicycleEcco come è fatto:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
Per questo usiamo una parola speciale. super(); Ora, se vogliamo creare un oggetto SportSeat, nulla ci impedirà di farlo:
public class Main {

   public static void main(String[] args) {

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

   }
}
Uff, la lezione si è rivelata piuttosto impegnativa :) Ma hai imparato un sacco di cose nuove! Ora è il momento di risolvere alcuni problemi! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION