Hallo! Heute schauen wir uns einen wichtigen Mechanismus an: die Vererbung in verschachtelten Klassen. Haben Sie jemals darüber nachgedacht, was Sie tun würden, wenn Sie eine verschachtelte Klasse dazu bringen müssten, eine andere Klasse zu erben? Wenn nicht, glauben Sie mir: Diese Situation kann verwirrend sein, weil es viele Nuancen gibt.
- Lassen wir eine verschachtelte Klasse eine Klasse erben? Oder lassen wir eine Klasse eine verschachtelte Klasse erben?
- Ist die untergeordnete/übergeordnete Klasse eine gewöhnliche öffentliche Klasse oder auch eine verschachtelte Klasse?
- Welche Art von verschachtelten Klassen verwenden wir schließlich in all diesen Situationen?
Statisch verschachtelte Klassen
Ihre Vererbungsregeln sind die einfachsten. Hier können Sie fast alles tun, was Ihr Herz begehrt. Eine statisch verschachtelte Klasse kann Folgendes erben:- eine gewöhnliche Klasse
- eine statisch verschachtelte Klasse, die in einer äußeren Klasse oder ihren Vorgängern deklariert wird
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;
}
}
}
Versuchen wir, den Code zu ändern und eine Drawing
statische verschachtelte Klasse und ihren Nachkommen zu erstellen – 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;
}
}
}
Wie Sie sehen, kein Problem. Wir können die Klasse sogar herausziehen Drawing
und sie zu einer gewöhnlichen öffentlichen Klasse anstelle einer statischen verschachtelten Klasse machen – es wird sich nichts ändern.
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;
}
}
}
Wir verstehen das. Aber welche Klassen können eine statisch verschachtelte Klasse erben? Praktisch alle! Verschachtelt/nicht verschachtelt, statisch/nicht statisch – das spielt keine Rolle. Hier lassen wir die Boeing737Drawing
innere Klasse die Drawing
statische verschachtelte Klasse erben:
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;
}
}
}
Sie können eine Instanz wie Boeing737Drawing
folgt erstellen:
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());
}
}
Obwohl unsere Boeing737Drawing
Klasse eine statische Klasse erbt, ist sie selbst nicht statisch! Daher wird immer eine Instanz der äußeren Klasse benötigt. Wir können die Boeing737Drawing
Klasse aus der Boeing737
Klasse entfernen und sie zu einer einfachen öffentlichen Klasse machen. Nichts verändert sich. Es kann weiterhin die Drawing
statische verschachtelte Klasse erben.
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;
}
Der einzig wichtige Punkt ist, dass wir in diesem Fall die statische Variable öffentlich machen müssen maxPassengersCount
. Wenn es privat bleibt, hat eine normale öffentliche Klasse keinen Zugriff darauf. Wir haben statische Klassen herausgefunden! :) Kommen wir nun zu den inneren Klassen. Es gibt drei Typen: einfache innere Klassen, lokale Klassen und anonyme innere Klassen. Gehen wir noch einmal vom Einfachen zum Komplexen :)
Anonyme innere Klassen
Eine anonyme innere Klasse kann keine andere Klasse erben. Keine andere Klasse kann eine anonyme Klasse erben. Einfacher geht es nicht! :) :)Lokale Klassen
Lokale Klassen (falls Sie es vergessen haben) werden innerhalb eines Codeblocks einer anderen Klasse deklariert. Am häufigsten geschieht dies innerhalb einer Methode der äußeren Klasse. Logischerweise können nur andere lokale Klassen innerhalb derselben Methode (oder desselben Codeblocks) eine lokale Klasse erben. Hier ist ein Beispiel:
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
}
}
Dies ist der Code aus unserer Lektion über lokale Klassen. Unsere Nummernvalidatorklasse verfügt über eine PhoneNumber
lokale Klasse. Wenn wir zwei unterschiedliche Einheiten darstellen müssen, beispielsweise eine Mobiltelefonnummer und eine Festnetznummer, können wir dies nur innerhalb derselben Methode tun. Der Grund ist einfach: Der Gültigkeitsbereich einer lokalen Klasse ist auf die Methode (Codeblock) beschränkt, in der sie deklariert ist. Daher können wir es nicht extern verwenden (auch nicht für die Klassenvererbung). Allerdings sind die Möglichkeiten der Vererbung innerhalb der lokalen Klasse selbst viel größer! Eine lokale Klasse kann Folgendes erben:
- Eine gewöhnliche Klasse.
- Eine innere Klasse, die in derselben Klasse wie die lokale Klasse oder in einem ihrer Vorfahren deklariert ist.
- Eine andere lokale Klasse, die in derselben Methode (Codeblock) deklariert wurde.
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
}
}
Hier haben wir die PhoneNumber
Klasse aus der validatePhoneNumber()
Methode entfernt und sie zu einer inneren Klasse anstelle einer lokalen Klasse gemacht. Dies hindert uns nicht daran, unsere beiden lokalen Klassen dazu zu bringen, es zu erben. Beispiel 2 – „... oder in den Vorfahren dieser Klasse.“ Das ist jetzt schon interessanter. Wir können PhoneNumber
in der Vererbungskette noch weiter nach oben vordringen. Lassen Sie uns eine abstrakte AbstractPhoneNumberValidator
Klasse deklarieren, die zum Vorfahren unserer PhoneNumberValidator
Klasse wird:
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;
}
}
}
Wie Sie sehen, haben wir es nicht nur deklariert, sondern auch die PhoneNumber
innere Klasse hinein verschoben. PhoneNumberValidator
In seinem Nachkommen können in Methoden deklarierte lokale Klassen jedoch PhoneNumber
problemlos erben!
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
}
}
Aufgrund der Vererbungsbeziehung „sehen“ die lokalen Klassen innerhalb einer Nachkommenklasse die inneren Klassen innerhalb eines Vorfahren. Und zum Schluss kommen wir zur letzten Gruppe :)
Innere Klassen
Eine innere Klasse, die in derselben äußeren Klasse (oder in ihrem Nachkommen) deklariert ist, kann eine andere innere Klasse erben. Lassen Sie uns dies anhand unseres Beispiels mit Fahrrädern aus der Lektion über innere Klassen untersuchen.
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
}
}
Hier haben wir die Seat
innere Klasse innerhalb der Bicycle
Klasse deklariert. Eine besondere Art von Rennsitz, SportSeat
erbt es. Aber wir könnten einen separaten „Rennrad“-Typ erstellen und ihn einer separaten Klasse zuordnen:
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!");
}
}
}
Dies ist auch eine Option. Die innere Klasse des Nachkommen ( SportBicycle.SportSeat
) „sieht“ die inneren Klassen des Vorfahren und kann sie erben. Das Erben innerer Klassen hat eine sehr wichtige Funktion! In den beiden vorherigen Beispielen SportSeat
war unsere Klasse eine innere Klasse. SportSeat
Was aber, wenn wir uns entscheiden , eine gewöhnliche öffentliche Klasse zu erstellen , die gleichzeitig die Seat
innere Klasse erbt?
// 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!");
}
}
Es ist ein Fehler aufgetreten! Kannst du erraten warum? :) Es ist alles unkompliziert. Als wir über die Bicycle.Seat
innere Klasse sprachen, erwähnten wir, dass ein Verweis auf eine Instanz der äußeren Klasse implizit an den Konstruktor der inneren Klasse übergeben wird. Das bedeutet, dass Sie kein Seat
Objekt erstellen können, ohne ein Bicycle
Objekt zu erstellen. Aber wie sieht es mit der Schaffung eines aus SportSeat
? Im Gegensatz zu Seat
verfügt es nicht über diesen integrierten Mechanismus, um dem Konstruktor implizit einen Verweis auf eine Instanz der äußeren Klasse zu übergeben. Dennoch Bicycle
können wir ohne ein Objekt kein SportSeat
Objekt erschaffen, genau wie im Fall von Seat
. Daher bleibt uns nur noch eines zu tun – dem SportSeat
Konstruktor explizit eine Referenz auf ein Bicycle
Objekt zu übergeben. So geht's:
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!");
}
}
Wir rufen den Superklassenkonstruktor mit auf super();
. Wenn wir nun ein Objekt erstellen möchten SportSeat
, hindert uns nichts daran, dies zu tun:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Puh! Diese Lektion war ziemlich lang :) Aber du hast viel gelernt! Jetzt ist es an der Zeit, einige Aufgaben zu lösen! :) :)
GO TO FULL VERSION