CodeGym /Java Blog /Random-IT /Esempi di ereditarietà di classi nidificate
John Squirrels
Livello 41
San Francisco

Esempi di ereditarietà di classi nidificate

Pubblicato nel gruppo Random-IT
CIAO! Oggi vedremo un meccanismo importante: l'ereditarietà nelle classi nidificate. Hai mai pensato a cosa faresti se avessi bisogno di fare in modo che una classe nidificata erediti un'altra classe. In caso contrario, credimi: questa situazione può creare confusione, perché ci sono molte sfumature.
  1. Stiamo facendo in modo che una classe annidata erediti una classe? O stiamo facendo in modo che una classe erediti una classe nidificata?
  2. La classe figlio/genitore è una normale classe pubblica o è anche una classe nidificata?
  3. Infine, che tipo di classi nidificate usiamo in tutte queste situazioni?
Ci sono così tante possibili risposte a tutte queste domande che ti girerà la testa :) Come sai, possiamo risolvere un problema complesso dividendolo in parti più semplici. Facciamolo. Consideriamo a turno ogni gruppo di classi annidate da due prospettive: chi può ereditare ogni tipo di classe annidata e chi può ereditare. Cominciamo con le classi nidificate statiche.

Classi nidificate statiche

Esempi di ereditarietà di classi nidificate - 2Le loro regole di eredità sono le più semplici. Qui puoi fare quasi tutto ciò che il tuo cuore desidera. Una classe annidata statica può ereditare:
  • una classe ordinaria
  • una classe nidificata statica dichiarata in una classe esterna o nei suoi predecessori
Ricordiamo un esempio della nostra 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 cambiare il codice e creare una Drawingclasse nidificata statica e 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 persino estrarre la Drawingclasse e renderla una normale classe pubblica anziché 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;
       }
   }
}
Lo capiamo. Ma quali classi possono ereditare una classe nidificata statica? Praticamente qualsiasi! Nidificato/non nidificato, statico/non statico: non importa. Qui facciamo in modo che la Boeing737Drawingclasse interna erediti la Drawingclasse annidata statica:

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 di Boeing737Drawingquesto tipo:

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 Boeing737Drawingclasse erediti una classe statica, non è essa stessa statica! Di conseguenza, avrà sempre bisogno di un'istanza della classe esterna. Possiamo rimuovere la Boeing737Drawingclasse dalla Boeing737classe e renderla una semplice classe pubblica. Niente cambia. Può ancora ereditare la Drawingclasse nidificata statica.

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;
   
}
L'unico punto importante è che in questo caso dobbiamo rendere maxPassengersCountpubblica la variabile statica. Se rimane privato, una normale classe pubblica non avrà accesso ad esso. Abbiamo scoperto le classi statiche! :) Ora passiamo alle classi interne. Sono disponibili in 3 tipi: classi interne semplici, classi locali e classi interne anonime. Esempi di ereditarietà di classi nidificate - 3Di nuovo, passiamo dal semplice al complesso :)

Classi interne anonime

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

Classi locali

Le classi locali (nel caso l'avessi dimenticato) sono dichiarate all'interno di un blocco di codice di un'altra classe. Molto spesso, ciò accade all'interno di un metodo della classe esterna. Logicamente, solo altre classi locali all'interno dello stesso metodo (o blocco di codice) possono ereditare 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 {
          
          
       }

       // ...number validation code
   }
}
Questo è il codice della nostra lezione sulle classi locali. La nostra classe di convalida dei numeri ha una PhoneNumberclasse locale. Se ci serve per rappresentare due entità distinte, ad esempio un numero di cellulare e un numero di telefono fisso, possiamo farlo solo all'interno dello stesso metodo. Il motivo è semplice: l'ambito di una classe locale è limitato al metodo (blocco di codice) in cui è dichiarato. Di conseguenza, non saremo in grado di utilizzarlo esternamente (incluso per l'ereditarietà delle classi). Tuttavia, le possibilità di eredità all'interno della stessa classe locale sono molto più ampie! Una classe locale può ereditare:
  1. Una classe ordinaria.
  2. Una classe interna dichiarata nella stessa classe della classe locale o in uno dei suoi predecessori.
  3. Un'altra classe locale dichiarata nello stesso metodo (blocco di codice).
Il primo e il terzo punto sembrano ovvi, ma il secondo è un po' confuso :/ Diamo un'occhiata a due esempi. Esempio 1 — "Fare in modo che una classe locale erediti 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);
           }
       }

       // ...number validation code
   }
}
Qui abbiamo rimosso la PhoneNumberclasse dal validatePhoneNumber()metodo e l'abbiamo resa una classe interna anziché una classe locale. Questo non ci impedisce di farla ereditare alle nostre 2 classi locali. Esempio 2 — "... o negli antenati di questa classe." Ora questo è già più interessante. Possiamo spostarci PhoneNumberancora più in alto nella catena ereditaria. Dichiariamo una AbstractPhoneNumberValidatorclasse astratta, che diventerà l'antenata della nostra PhoneNumberValidatorclasse:

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 l'abbiamo solo dichiarato, ma abbiamo anche spostato la PhoneNumberclasse interna. Tuttavia, nel suo discendente PhoneNumberValidator, le classi locali dichiarate nei metodi possono ereditare PhoneNumbersenza alcun problema!

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
   }
}
A causa della relazione di ereditarietà, le classi locali all'interno di una classe discendente "vedono" le classi interne all'interno di un antenato. E infine, passiamo all'ultimo gruppo :)

Classi interne

Una classe interna dichiarata nella stessa classe esterna (o nella sua discendente) può ereditare un'altra classe interna. Esploriamolo usando il nostro esempio con le biciclette della lezione sulle classi interne.

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
   }
}
Qui abbiamo dichiarato la Seatclasse interna all'interno della Bicycleclasse. Un tipo speciale di sedile da corsa, SportSeatlo eredita. Ma potremmo creare un tipo di "bicicletta da corsa" separato e inserirlo in una classe separata:

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!");
       }
   }
}
Anche questa è un'opzione. La classe interna del discendente ( SportBicycle.SportSeat) "vede" le classi interne dell'antenato e può ereditarle. L'ereditarietà delle classi interne ha una caratteristica molto importante! Nei due esempi precedenti, la nostra SportSeatclasse era una classe interna. Ma cosa succede se decidiamo di creare SportSeatuna classe pubblica ordinaria che contemporaneamente erediti la Seatclasse interna?

// 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!");
   }
}
Abbiamo un errore! Riesci a indovinare perché? :) È tutto semplice. Quando abbiamo parlato della Bicycle.Seatclasse interna, abbiamo accennato al fatto che un riferimento a un'istanza della classe esterna viene implicitamente passato al costruttore della classe interna. Ciò significa che non puoi creare un Seatoggetto senza creare un Bicycleoggetto. Ma per quanto riguarda la creazione di un SportSeat? A differenza di Seat, non ha questo meccanismo integrato per passare implicitamente al costruttore un riferimento a un'istanza della classe esterna. Tuttavia, senza un Bicycleoggetto, non possiamo creare un SportSeatoggetto, proprio come nel caso di Seat. Pertanto, rimane solo una cosa da fare: passare esplicitamente al SportSeatcostruttore un riferimento a un Bicycleoggetto. Ecco come farlo:

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!");
   }
}
Chiamiamo il costruttore della superclasse usando super(); Ora, se vogliamo creare un SportSeatoggetto, 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! Questa lezione è stata piuttosto lunga :) Ma hai imparato molto! Ora è il momento di risolvere alcuni compiti! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION