Für den heutigen und letzten Blogartikel dieses Crashkurses wird in Java Vererbung auf dem Lehrplan stehen. Dabei werden wir uns zunächst ansehen, was die Vererbung ist und anschließend im Code zusammen ein Praxisbeispiel programmieren, bei dem du erfahren wirst, warum sie so nützlich ist.
Inhaltsverzeichnis
1. Anmerkung
Dass wir hiermit beim letzten Beitrag dieses Crashkurses angekommen sind, bedeutet natürlich nicht, dass keine weiteren Artikel mehr zur Programmiersprache Java folgen werden. Wir schließen damit lediglich diesen 24-teiligen Einsteigerkurs ab, der dazu dienen sollte, die Grundkonzepte der Java-Programmierung zu verstehen.
Wenn du auch die vorherigen Beiträge dieses Java-Crashkurses gelesen hast, solltest du bereits jetzt mit dem nötigen Wissen ausgestattet sein, um deine ersten eigenen kleinen Programme zu schreiben! Du kannst also schon nach diesem Crashkurs damit beginnen, das Gelernte in der Praxis anzuwenden, um so deine Fähigkeiten auf das nächste Level zu bringen.
Da noch weitere interessante Beiträge zur Java-Entwicklung folgen werden, lohnt es sich, auch in Zukunft noch auf unserem Blog vorbeizuschauen.
Lass uns nun aber mit dem eigentlichen Thema dieses Beitrags starten:
2. Was ist Vererbung?
Vererbung ermöglicht in Java das Erweitern von Klassen, ohne diese direkt zu verändern.
Haben wir also beispielsweise eine Klasse programmiert, die wir um neue Attribute und Methoden erweitern möchten, können wir das ab heute umsetzen, ohne unsere Klasse dafür verändern zu müssen.
Was ist der Vorteil an der Vererbung?
Wenn wir eine Klasse bereits vollumfänglich getestet haben und sie bereits im Betrieb eingesetzt wird, ist es ziemlich ärgerlich, bei jeder neuen Änderung die ganze Klasse erneut testen zu müssen.
Aus diesem Grund kommt in Java Vererbung ins Spiel. Damit wir unsere Programme nachträglich problemlos erweitern können, auch wenn sie bereits fertig programmiert sind.
Man kann an dieser Stelle noch weit in die Materie eintauchen, allerdings gehört das zum Thema Software-Design, worauf wir erst in einem anderen Beitrag zu sprechen kommen werden.
Was du dir also merken solltest: Die Vererbung ermöglicht das Erweitern von Klassen.
Welches Grundprinzip steckt hinter der Java Vererbung?
Das Grundprinzip ist, eine neue Klasse zu erstellen, die eine bestehende Klasse erweitern soll. Das macht sie, indem sie alle Attribute und Methoden von der Klasse erbt, die wir erweitern möchten.
Anschließend programmieren wir in die neue Klasse noch zusätzliche Funktionalitäten wie Attribute und/oder Methoden.
Das ist auch schon das ganze Konzept hinter der Vererbung.
Im Moment klingt das womöglich noch sehr abstrakt, zumindest ging es mir damals so, als ich zum ersten Mal von der Vererbung in Java gehört habe. Um das ganze Thema also zu veranschaulichen, sehen wir uns nun mal ein Beispiel in der Praxis an.
3. Die Java Vererbung in der Praxis
Für unser Beispiel habe ich ein neues Projekt angelegt und darin drei Klassen programmiert. Dazu gehört die Klasse Program, die wie gewohnt die Main-Methode enthält.
Darüber hinaus gibt es noch die Klasse Dog:
Sowie die Klasse Cat:
Es existiert damit eine Klasse, die einen Hund repräsentiert und eine weitere Klasse, die eine Katze darstellt.
In der Klasse Dog sehen wir zwei Attribute: eines für den Namen (name) und eines für das Alter (age) des Hundes. Schließlich hat jeder Hund sowohl einen Namen als auch ein Alter.
private String name;
private int age;
Außerdem gibt es in der Klasse Hund die Methode sleep, die auf der Konsole den String „Schlafen…“ ausgibt.
public void sleep() {
System.out.println("Schlafen…");
}
Da ein Hund auch bellen kann, finden wir in der Dog-Klasse darüber hinaus die Methode bark, in der wir „Wau wau“ ausgeben lassen.
public void bark() {
System.out.println("Wau wau");
}
Im nächsten Schritt werfen wir einen Blick auf die Klasse Cat. Auch darin sehen wir ein Attribut für den Namen sowie ein Attribut für das Alter der Katze:
private String name;
private int age;
Hier gibt es ebenfalls die Methode sleep, die exakt den gleichen Zweck hat wie die sleep-Methode der Klasse Dog. Nämlich den, auf der Konsole „Schlafen…“ auszugeben:
public void sleep() {
System.out.println("Schlafen…");
}
Außerdem besitzt die Cat-Klasse noch die Methode purr, die das Schnurren der Katze simulieren soll:
public void purr() {
System.out.println("Schnurr Schnurr");
}
Denn bekanntermaßen bellt eine Katze nicht, sondern schnurrt.
Jetzt fällt dir möglicherweise bereits auf, dass das Ganze hier nicht so optimal gelöst ist. Warum? Weil es sehr viel Redundanz in unserem Code gibt. Redundanz bedeutet in diesem Fall, dass wir die gleichen Attribute und Methoden überflüssigerweise mehrmals im Code eingesetzt haben.
Redundanz ist nie gut, da unser Programm damit fehleranfälliger ist und uns zeigt, dass wir eine bessere Lösung finden können.
Diese bessere Lösung heißt an dieser Stelle: Vererbung.
Was ist an den Klassen Dog und Cat gleich?
Die Attribute String name und int age sind sowohl in der Klasse Dog als auch in der Klasse Cat vertreten. Da sie vollkommen identisch sind, sind sie auch redundant.
Des Weiteren kommt die Methode sleep bei beiden Klassen zum Einsatz, da Hunde und Katzen gleichermaßen schlafen können.
Die einzigen spezifischen Methoden, die sich in beiden Klassen unterscheiden, sind die purr-Methode, die für das Schnurren der Katze verantwortlich ist, sowie die bark-Methode, die den Hund bellen lässt.
Vererbung Schritt 1: Eine neue Klasse erstellen
Um die überflüssigen Wiederholungen im Code entfernen zu können, kommt jetzt die Java Vererbung ins Spiel. Hierzu erstellen wir uns erst mal eine weitere Klasse:
Dieser Klasse geben wir den allgemeineren Namen „Animal“:
Der Klassenname „Animal“ passt deshalb, weil sowohl Hunde als auch Katzen Tiere sind.
Außerdem lässt er bereits vermuten, dass die Klasse allgemeiner gehalten ist als die Klassen Dog und Cat.
Oder anders gesagt: Die Klassen Dog und Cat sind spezifischer als die Klasse Animal, weil sie das jeweilige Tier noch genauer beschreiben. Bei der Klasse Dog wissen wir schon im Vorhinein, dass es sich um einen Hund und nicht um ein x-beliebiges anderes Tier handelt.
Vererbung Schritt 2: Redundanten Code löschen und in der neuen Klasse einsetzen
Die Vererbung hat jetzt folgenden Sinn. Wir können nun alle Attribute und Methoden, die in beiden Klassen gleich sind, kopieren und aus den Klassen Dog und Cat löschen. Anschließend fügen wir diese in der Klasse Animal ein:
Das bedeutet, dass die Klasse Animal nun die Attribute name und age enthält, da jedes Tier einen Namen und ein Alter besitzt. Weil auch jedes Tier schlafen kann, haben wir die sleep-Methode ebenfalls in der Animal-Klasse implementiert.
In der Dog-Klasse ist spezifisch, dass ein Hund noch zusätzlich bellen kann. Wir können die bark-Methode natürlich nicht in die Animal-Klasse programmieren, da schließlich nicht jedes Tier bellen kann. Damit ist die Methode exklusiv Bestandteil der Hunde-Klasse und ergibt auch nur darin tatsächlich Sinn.
An diesem Punkt werden wir die Vererbung herstellen. Wie ich bereits erwähnt habe, können wir mithilfe der Java Vererbung eine Klasse erweitern. Genau das möchten wir jetzt bei der Klasse Animal umsetzen.
Vererbung Schritt 3: Eine bestehende Klasse von der neuen Klasse erben lassen
Animal besitzt zwei Attribute und die sleep-Methode. Wir möchten die Klasse jetzt speziell auf den Hund bezogen um die Methode bark erweitern.
Dadurch bietet sich an, dass wir die Klasse Dog von der Klasse Animal erben lassen. Realisieren können wir das ganz einfach folgendermaßen.
Wir bewegen uns mit dem Cursor neben den Klassennamen „Dog“, ergänzen ein Leerzeichen und schreiben dann „extends“. Dabei handelt es sich um das Schlüsselwort, das wir für die Vererbung benötigen. Anschließend folgt der Name der Klasse, von der die Klasse Dog erben soll. In unserem Fall ist das die Klasse Animal:
Jetzt speichern wir das Programm und haben damit bewirkt, dass die Klasse Dog alle Attribute und Methoden der Animal-Klasse erbt.
Diese sind ab sofort neben der bark-Methode in der Klasse Dog verfügbar.
Wir können nun in der Klasse Program zum Beispiel ein Objekt vom Typ Dog erstellen und im Anschluss die sleep-Methode aufrufen:
Dog dog = new Dog();
dog.sleep();
Wie wir sehen, erscheint keine Fehlermeldung und beim Ausführen des Programms taucht auch schon der String „Schlafen…“ in der Konsole auf:
Output:
Schlafen...
Somit funktioniert unser Code!
Wir rufen also auf dem Objekt vom Typ Dog die sleep-Methode auf, obwohl sie in der Dog-Klasse gar nicht definiert wurde. Lediglich die bark-Methode haben wir darin erstellt:
Da aber die Klasse Dog von der Klasse Animal erbt und in dieser die Methode sleep enthalten ist, haben wir die Möglichkeit, sie aufzurufen.
Das ist im Prinzip das ganze Konzept, das sich hinter dem Begriff „Vererbung“ verbirgt. Worauf wir hier allerdings wieder achten müssen, sind die Sichtbarkeitsmodifizierer.
Die Sichtbarkeitsmodifizierer bei der Vererbung
public class Animal {
private String name;
private int age;
In unserer Animal-Klasse haben wir die Attribute als private deklariert, was bedeutet, dass wir darin Getter und Setter für die Attribute hinzufügen müssen:
Da die gerade erzeugten Getter und Setter wieder public sind, werden diese auch ordnungsgemäß an die Klasse Dog vererbt und wir haben die Möglichkeit, von außen darauf zuzugreifen.
Das heißt, wir können jetzt beispielsweise in der Main-Methode dem Hund einen Namen wie etwa „Bello“ zuweisen und diesen anschließend mit dog.getName() auf der Konsole ausgeben lassen:
Dog dog = new Dog();
dog.sleep();
dog.setName("Bello");
System.out.println(dog.getName());
Beim Ausführen des Programms wird nun auch „Bello“ ausgegeben:
Output:
Schlafen…
Bello
Die Sichtbarkeitsmodifizierer bleiben damit auch bei der Vererbung aktiv, worauf wir immer achten sollten.
Die Klasse Cat von der Klasse Animal erben lassen
Jetzt können wir uns in die Klasse Cat bewegen und auch dort die Attribute name und age sowie die sleep-Methode herauslöschen. Da auch die Katze ein Tier ist, kann die Klasse Cat ebenfalls mit dem Schlüsselwort extends von der Klasse Animal erben:
Nur die spezifische Methode purr, welche die Klasse Animal noch erweitert, implementieren wir in der Klasse Cat. Jetzt könnten wir in der Main-Methode auch eine Katze erzeugen, die sleep-Methode ausführen und so weiter.
4. Warum die Vererbung so nützlich ist
Wie du siehst, haben wir durch die Animal-Klasse zwar eine Klasse mehr, allerdings ist das Programm dadurch wesentlich besser strukturiert.
Denn es gibt keinen redundanten Code mehr, da jede Funktion nur einmal im Programm definiert ist.
Es steht also nicht in jeder einzelnen Tierklasse String name, int age und die Methode sleep. Und das ist ein großer Vorteil, wenn wir bedenken, dass Klassen sehr groß werden können. Stellen wir uns nur mal vor, die Klasse Cat würde 200 Attribute und 2000 Methoden besitzen. Wenn von den 2000 Methoden nun 1000 für alle Tiere gelten, dann müssen wir diese jetzt nicht in jeder Klasse schreiben, sondern definieren stattdessen mit der Klasse Animal eine sogenannte Elternklasse.
5. Was sind Eltern- und Kindklassen?
Damit habe ich auch schon einen von mehreren wichtigen Begriffen genannt, die man beim Thema Vererbung in Java kennen sollte.
Die Klasse Animal, also die Klasse, von der die anderen Klassen erben, bezeichnet man als Super-Klasse oder Eltern-Klasse. Neben diesen Begriffen gibt es noch weitere Synonyme. Welche Bezeichnung du verwenden möchtest, kannst du selbst entscheiden.
Die Klassen, die von einer Klasse erben, nennt man Sub-Klassen oder Kind-Klassen.
Dieses Prinzip der Vererbung lässt sich jetzt immer weiterführen. Wir könnten also neben der Klasse Dog noch spezifischere Klassen bauen, wie zum Beispiel eine Klasse Labrador und eine Klasse Schäferhund, die dann wiederum von der Klasse Dog erben.
Für diese beiden neuen Klassen ist dann die Klasse Dog die Super-Klasse. Genauso ist die Klasse Labrador, die Kind-Klasse für die Klasse Dog. Letztere hat wiederum die Klasse Animal als Super-Klasse bzw. Elternklasse.
Man bezeichnet das ganze Konstrukt als Vererbungshierarchie.
Das Programm beschränkt sich damit nicht jedes Mal nur auf zwei Ebenen, also auf eine Eltern- und eine Kind-Klasse, sondern lässt sich immer feiner gliedern.
6. Zusammenfassung
Wenn wir in unserem Programm etwas vererben möchten, erstellen wir eine Klasse, von der geerbt werden soll und ergänzen anschließend beliebig viele weitere Klassen, die die Klasse erweitern, von der geerbt wird.
Im Fall unseres Beispiels die Klasse Dog um die Methode bark und dann schreiben wir neben den Klassennamen das Schlüsselwort extends und den Namen der Klasse, von der geerbt werden soll. Den Rest erledigt Java intern für uns.
Die Vererbung steht uns als unheimlich mächtiges Feature zur Verfügung, mit welchem sich natürlich auch noch viele weitere nützliche Dinge umsetzen lassen. In diesem Beitrag sollte es allerdings erst mal darum gehen, ein Grundverständnis zum Thema Vererbung zu erlangen.
An dieser Stelle sind wir auch schon am Ende des Crashkurses angekommen! Es hat mir sehr viel Spaß gemacht, deinen Weg in die Programmiersprache Java zu begleiten und ich hoffe, dass dir dadurch der Einstieg deutlich erleichtert wurde.