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.
Le 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:
Di nuovo, passiamo dal semplice al complesso :)
- Stiamo facendo in modo che una classe annidata erediti una classe? O stiamo facendo in modo che una classe erediti una classe nidificata?
- La classe figlio/genitore è una normale classe pubblica o è anche una classe nidificata?
- Infine, che tipo di classi nidificate usiamo in tutte queste situazioni?
Classi nidificate statiche

- una classe ordinaria
- una classe nidificata statica dichiarata in una classe esterna o nei suoi predecessori
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 Drawing
classe 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 Drawing
classe 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 Boeing737Drawing
classe interna erediti la Drawing
classe 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 Boeing737Drawing
questo 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 Boeing737Drawing
classe erediti una classe statica, non è essa stessa statica! Di conseguenza, avrà sempre bisogno di un'istanza della classe esterna. Possiamo rimuovere la Boeing737Drawing
classe dalla Boeing737
classe e renderla una semplice classe pubblica. Niente cambia. Può ancora ereditare la Drawing
classe 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 maxPassengersCount
pubblica 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. 
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 PhoneNumber
classe 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:
- Una classe ordinaria.
- Una classe interna dichiarata nella stessa classe della classe locale o in uno dei suoi predecessori.
- Un'altra classe locale dichiarata nello stesso metodo (blocco di codice).
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 PhoneNumber
classe 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 PhoneNumber
ancora più in alto nella catena ereditaria. Dichiariamo una AbstractPhoneNumberValidator
classe astratta, che diventerà l'antenata della nostra PhoneNumberValidator
classe:
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 PhoneNumber
classe interna. Tuttavia, nel suo discendente PhoneNumberValidator
, le classi locali dichiarate nei metodi possono ereditare PhoneNumber
senza 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 Seat
classe interna all'interno della Bicycle
classe. Un tipo speciale di sedile da corsa, SportSeat
lo 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 SportSeat
classe era una classe interna. Ma cosa succede se decidiamo di creare SportSeat
una classe pubblica ordinaria che contemporaneamente erediti la Seat
classe 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.Seat
classe 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 Seat
oggetto senza creare un Bicycle
oggetto. 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 Bicycle
oggetto, non possiamo creare un SportSeat
oggetto, proprio come nel caso di Seat
. Pertanto, rimane solo una cosa da fare: passare esplicitamente al SportSeat
costruttore un riferimento a un Bicycle
oggetto. 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 SportSeat
oggetto, 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! :)
GO TO FULL VERSION