Vererbungshilfe in Java

Leitfaden zur Vererbung in Java

1. Überblick

Eines der Grundprinzipien der objektorientierten Programmierung -inheritance – enables us to reuse existing code or extend an existing type.

Einfach ausgedrückt, in Java kann eine Klasse eine andere Klasse und mehrere Schnittstellen erben, während eine Schnittstelle andere Schnittstellen erben kann.

In diesem Artikel beginnen wir mit der Notwendigkeit der Vererbung und gehen dazu über, wie die Vererbung mit Klassen und Schnittstellen funktioniert.

Anschließend wird erläutert, wie sich die Variablen- / Methodennamen und Zugriffsmodifikatoren auf die vererbten Mitglieder auswirken.

Und am Ende werden wir sehen, was es bedeutet, einen Typ zu erben.

2. Das Bedürfnis nach Vererbung

Stellen Sie sich vor, Sie bieten als Autohersteller Ihren Kunden mehrere Automodelle an. Auch wenn verschiedene Automodelle unterschiedliche Funktionen wie ein Schiebedach oder kugelsichere Fenster bieten, enthalten sie alle gemeinsame Komponenten und Funktionen wie Motor und Räder.

Es ist sinnvoll, ein grundlegendes Design zu erstellen und es zu erweitern, um ihre speziellen Versionen zu erstellen, anstatt jedes Automodell einzeln von Grund auf neu zu entwerfen.

Auf ähnliche Weise können wir mit der Vererbung eine Klasse mit grundlegenden Funktionen und Verhalten erstellen und ihre Spezialversionen erstellen, indem wir Klassen erstellen, die diese Basisklasse erben. Ebenso können Schnittstellen bestehende Schnittstellen erweitern.

Wir werden feststellen, dass mehrere Begriffe verwendet werden, um auf einen Typ zu verweisen, der von einem anderen Typ geerbt wird, insbesondere:

  • Ein Basistyp wird auch als übergeordneter oder übergeordneter Typ bezeichnet

  • Ein abgeleiteter Typ wird als erweiterter, untergeordneter oder untergeordneter Typ bezeichnet

3. Vererbung mit Klassen

3.1. Klasse erweitern

Eine Klasse kann eine andere Klasse erben und zusätzliche Mitglieder definieren.

Beginnen wir mit der Definition einer BasisklasseCar:

public class Car {
    int wheels;
    String model;
    void start() {
        // Check essential parts
    }
}

Die KlasseArmoredCar kann die Mitglieder der KlasseCar vonusing the keyword extends in its declaration erben:

public class ArmoredCar extends Car {
    int bulletProofWindows;
    void remoteStartCar() {
    // this vehicle can be started by using a remote control
    }
}

Classes in Java support single inheritance; Die KlasseArmoredCarkann nicht mehrere Klassen erweitern. Wenn kein Schlüsselwortextends vorhanden ist, erbt eine Klasse implizit die Klassejava.lang.Object.

3.2. Was wird geerbt?

Grundsätzlich erbt eine abgeleitete Klasse die Mitgliederprotected undpublic von der Basisklasse, die nichtstatic. sind. Außerdem die Mitglieder mitdefault undpackage Zugriff werden vererbt, wenn sich die beiden Klassen im selben Paket befinden.

Auf eine Basisklasse kann die abgeleiteten Klassen nicht auf den gesamten Code zugreifen.

Dieprivate undstatic Mitglieder einer Klasse werden nicht von einer abgeleiteten Klasse geerbt. Wenn Basisklasse und abgeleitete Klassen in separaten Paketen definiert sind, werden Mitglieder mitdefault oderpackage Zugriff in der Basisklasse nicht in die abgeleitete Klasse geerbt.

3.3. Greifen Sie über eine abgeleitete Klasse auf übergeordnete Klassenmitglieder zu

Es ist einfach. Verwenden Sie sie einfach (wir benötigen keinen Verweis auf die Basisklasse, um auf ihre Mitglieder zugreifen zu können). Hier ein kurzes Beispiel:

public class ArmoredCar extends Car {
    public String registerModel() {
        return model;
    }
}

3.4. Versteckte Mitglieder der Basisklasseninstanz

Was passiert, wenn sowohl unsere Basisklasse als auch unsere abgeleitete Klasse eine Variable oder Methode mit demselben Namen definieren? Mach dir keine Sorgen; wir können immer noch auf beide zugreifen. Wir müssen Java jedoch unsere Absicht klar machen, indem wir der Variablen oder Methode die Schlüsselwörterthis odersuper voranstellen.

Das Schlüsselwortthisbezieht sich auf die Instanz, in der es verwendet wird. Das Schlüsselwortsuperbezieht sich (wie es offensichtlich erscheint) auf die Instanz der übergeordneten Klasse:

public class ArmoredCar extends Car {
    private String model;
    public String getAValue() {
        return super.model;   // returns value of model defined in base class Car
        // return this.model;   // will return value of model defined in ArmoredCar
        // return model;   // will return value of model defined in ArmoredCar
    }
}

Viele Entwickler verwenden die Schlüsselwörterthis undsuper, um explizit anzugeben, auf welche Variable oder Methode sie sich beziehen. Wenn Sie sie jedoch mit allen Mitgliedern verwenden, kann unser Code unübersichtlich wirken.

3.5. Versteckte statische Mitglieder der Basisklasse

Was passiert, wenn unsere Basisklasse und abgeleitete Klassen statische Variablen und Methoden mit demselben Namen definieren? Können wir von der Basisklasse in der abgeleiteten Klasse auf einstatic-Mitglied zugreifen, wie wir es für die Instanzvariablen tun?

Lassen Sie uns anhand eines Beispiels herausfinden:

public class Car {
    public static String msg() {
        return "Car";
    }
}
public class ArmoredCar extends Car {
    public static String msg() {
        return super.msg(); // this won't compile.
    }
}

Nein, das können wir nicht. Die statischen Member gehören zu einer Klasse und nicht zu Instanzen. Daher können wir das nicht statische Schlüsselwortsuperinmsg()nicht verwenden.

Da statische Member zu einer Klasse gehören, können wir den vorherigen Aufruf wie folgt ändern:

return Car.msg();

Betrachten Sie das folgende Beispiel, in dem sowohl die Basisklasse als auch die abgeleitete Klasse eine statische Methodemsg() mit derselben Signatur definieren:

public class Car {
    public static String msg() {
        return "Car";
    }
}
public class ArmoredCar extends Car {
    public static String msg() {
        return "ArmoredCar";
    }
}

So können wir sie nennen:

Car first = new ArmoredCar();
ArmoredCar second = new ArmoredCar();

Für den vorhergehenden Code gebenfirst.msg() "Auto undsecond.msg()" ArmoredCar "aus. Die aufgerufene statische Nachricht hängt vom Typ der Variablen ab, mit der auf die Instanz vonArmoredCarverwiesen wird.

4. Vererbung mit Schnittstellen

4.1. Implementieren mehrerer Schnittstellen

Obwohl Klassen nur eine Klasse erben können, können sie mehrere Schnittstellen implementieren.

Stellen Sie sich vor, dieArmoredCar, die wir im vorhergehenden Abschnitt definiert haben, sind für einen Superspion erforderlich. Das Produktionsunternehmen vonCardachte also daran, fliegende und schwimmende Funktionen hinzuzufügen:

public interface Floatable {
    void floatOnWater();
}
public interface Flyable {
    void fly();
}
public class ArmoredCar extends Car implements Floatable, Flyable{
    public void floatOnWater() {
        System.out.println("I can float!");
    }

    public void fly() {
        System.out.println("I can fly!");
    }
}

Im obigen Beispiel sehen wir die Verwendung des Schlüsselwortsimplements zum Erben von einer Schnittstelle.

4.2. Probleme mit Mehrfachvererbung

Mehrfachvererbung mit Schnittstellen ist in Java zulässig.

Bis Java 7 war dies kein Problem. Schnittstellen konnten nurabstract-Methoden definieren, dh Methoden ohne Implementierung. Wenn also eine Klasse mehrere Schnittstellen mit derselben Methodensignatur implementiert, war dies kein Problem. Die implementierende Klasse hatte schließlich nur eine Methode zu implementieren.

Lassen Sie uns sehen, wie sich diese einfache Gleichung mit der Einführung derdefault-Methoden in Schnittstellen mit Java 8 geändert hat.

Starting with Java 8, interfaces could choose to define default implementations for its methods (eine Schnittstelle kann immer nochabstract Methoden definieren). Dies bedeutet, dass die untergeordnete Klasse separate Implementierungen erbt, wenn eine Klasse mehrere Schnittstellen implementiert, die Methoden mit derselben Signatur definieren. Das klingt komplex und ist nicht erlaubt.

Java verbietet die Vererbung mehrerer Implementierungen derselben Methoden, die in separaten Schnittstellen definiert sind.

Hier ist ein Beispiel:

public interface Floatable {
    default void repair() {
        System.out.println("Repairing Floatable object");
    }
}
public interface Flyable {
    default void repair() {
        System.out.println("Repairing Flyable object");
    }
}
public class ArmoredCar extends Car implements Floatable, Flyable {
    // this won't compile
}

Wenn wir beide Schnittstellen implementieren möchten, müssen wir dierepair()-Methode überschreiben.

Wenn die Schnittstellen in den vorhergehenden Beispielen Variablen mit demselben Namen definieren, z. B.duration, können wir nicht auf sie zugreifen, ohne dem Variablennamen den Schnittstellennamen vorangestellt zu haben:

public interface Floatable {
    int duration = 10;
}
public interface Flyable {
    int duration = 20;
}
public class ArmoredCar extends Car implements Floatable, Flyable {

    public void aMethod() {
        System.out.println(duration); // won't compile
        System.out.println(Floatable.duration); // outputs 10
        System.out.println(Flyable.duration); // outputs 20
    }
}

4.3. Schnittstellen Erweitern anderer Schnittstellen

Eine Schnittstelle kann mehrere Schnittstellen erweitern. Hier ist ein Beispiel:

public interface Floatable {
    void floatOnWater();
}
interface interface Flyable {
    void fly();
}
public interface SpaceTraveller extends Floatable, Flyable {
    void remoteControl();
}

Eine Schnittstelle erbt andere Schnittstellen mit dem Schlüsselwortextends. Klassen verwenden das Schlüsselwortimplements, um eine Schnittstelle zu erben.

5. Typ erben

Wenn eine Klasse eine andere Klasse oder eine andere Schnittstelle erbt, übernimmt sie nicht nur deren Mitglieder, sondern auch deren Typ. Dies gilt auch für eine Schnittstelle, die andere Schnittstellen erbt.

Dies ist ein sehr leistungsfähiges Konzept, mit dem Entwickler auf eine Schnittstelle (Basisklasse oder Schnittstelle) programmieren können, anstatt auf ihre Implementierungen (konkrete oder abgeleitete Klassen) zu programmieren.

Stellen Sie sich zum Beispiel eine Bedingung vor, in der eine Organisation eine Liste der Fahrzeuge ihrer Mitarbeiter führt. Natürlich können alle Mitarbeiter unterschiedliche Automodelle besitzen. Wie können wir also auf verschiedene Autoinstanzen verweisen? Hier ist die Lösung:

public class Employee {
    private String name;
    private Car car;

    // standard constructor
}

Da alle abgeleiteten Klassen vonCar den TypCar erben, können die abgeleiteten Klasseninstanzen mithilfe einer Variablen der KlasseCar referenziert werden:

Employee e1 = new Employee("Shreya", new ArmoredCar());
Employee e2 = new Employee("Paul", new SpaceCar());
Employee e3 = new Employee("Pavni", new BMW());

6. Fazit

In diesem Artikel haben wir einen Kernaspekt der Java-Sprache behandelt - wie die Vererbung funktioniert.

Wir erklären, wie Java die einfache Vererbung mit Klassen und die mehrfache Vererbung mit Schnittstellen unterstützt, und erläutern die Feinheiten der Funktionsweise des Mechanismus in der Sprache.

Wie immer ist der vollständige Quellcode für die Beispieleover on GitHub verfügbar.