Keylearnings:
- Was ist der Unterschied zwischen dem überschreiben und dem überladen einer Methode?
- Wie du Methoden überschreibst.
- Wie du Methoden überlädst.
- Wie du Methoden verkettest.
In meiner Schulklasse gab es drei Martins.
Im Sportunterricht war das ein Problem. Immer wenn jemand rief: „Martin fang den Ball!“, wusste man nie wer gemeint war.
Dasselbe Problem haben wir bei Java Methoden.
Denn in Java können wir mehrere Methoden mit gleichem Namen definieren.
Aber woher wissen wir bei einem Aufruf, welche Methode gemeint ist?
Genau mit diesem Problem wollen wir uns in diesem Artikel beschäftigen.
Der Unterschied zwischen Überschreiben und Überladen
Bei dieser Problematik unterscheiden wir zwei Fälle.
Zum einen können wir mehrere gleichnamige Methoden innerhalb der selben Klasse haben. In diesem Fall sprechen wir vom Überladen einer Methode.
Zum anderen, kann es in einer Vererbungshierarchie gleichnamige Methoden geben, d.h. eine Methode in einer Unterklasse hat den gleichen Namen wie die Methode einer Oberklasse. In diesem Fall sprechen wir vom Überschreiben einer Methode.
Schauen wir uns zunächst das Überladen einer Methode an.
Das Überladen einer Methode
Eine Methode wird durch drei Eigenschaften festgelegt:
- Methodenname
- Parameterliste
- Rückgabewert
Wenn wir eine Methode überladen bedeutet das, dass wir mindestens zwei Methoden vom gleichem Namen innerhalb einer Klasse haben.
Somit verbleibt uns zur Unterscheidung lediglich die Parameterliste und der Rückgabewert.
Und um es kurz zu machen! Eine Unterscheidung über den Rückgabewert funktioniert nicht.
Grund hierfür ist, dass wir in JAVA nicht gezwungen werden den Rückgabewert zu verarbeiten.
Betrachten wir folgende zwei Methoden,
private int arbeiteSchwer(){ //MACHE IRGENDWAS } private double arbeiteSchwer(){ //MACHE IRGENDWAS }
die sich lediglich im Rückgabetyp unterscheiden.
Versuchen wir die Methode mittels (d.h. ohne Verarbeitung des Rückgabetyps)
arbeiteSchwer();
aufzurufen, so kann der Compiler nicht feststellen, ob die Methode mit dem Rückgabewert int
oder double
aufgerufen werden soll und wir erhalten einen Compiler-Fehler.
Übrig bleibt also nur die Unterscheidung über die Parameterliste.
Parameterlisten gleichnamiger Methoden können sich zum einen in den Datentypen und zum anderen in der Anzahl der Parameter unterscheiden.
Machen wir uns das anhand einiger Beispiele klar und betrachten folgende Methoden-Definitionen:
1: public static void arbeiteSchwer(int x,double y,double z){ 2: System.out.println("arbeiteSchwer 1"); 3: } 4: public static void arbeiteSchwer(int x,double y){ 5: System.out.println("arbeiteSchwer 2"); 6:} 7: public static void arbeiteSchwer(double x,int y){ 8: System.out.println("arbeiteSchwer 3"); 9:}
Lust auf ein Quiz?
Welche Methode wird durch folgenden Aufruf ausgeführt?
arbeiteSchwer(6,7.0,6.7);
Das ist noch einfach!
Wir haben drei Argumente und nur eine Methode, die zu dieser Parameterliste passt und das ist die Methode aus Zeile Eins bis Drei. Und in der Tat erhalten wir die Programmausgabe:
arbeiteSchwer 1
Schauen wir uns einen weiteren Aufruf an:
arbeiteSchwer(3.1,5);
Diesmal haben wir zwei Argumente, dazu passt sowohl die Parameterliste der Methode in den Zeilen Vier bis Sechs als auch der Methode in den Zeilen Sieben bis Neun.
Um eine endgültige Entscheidung zu treffen, müssen wir uns also die Datentypen der Argumente ansehen.
Das erste Argument 3.1
ist vom Datentyp double
und das zweite Argument 5
ist ein Integer-Wert.
Somit kommt nur die Methode aus den Zeilen Sieben bis Neun in Frage. Und in der Tat ist die Programmausgabe:
arbeiteSchwer 3
Hast du eine Idee wie der Methodenaufruf lauten muss, damit die Methode aus den Zeilen Vier bis Sechs ausgeführt wird?
Jepp, wir müssen die Argumente einfach vertauschen.
arbeiteSchwer(5,3.1);
Jetzt ist das erste Argument vom Datentyp-Integer und das zweite Argument ein double
Wert was genau zur Parameterliste der gewünschten Methode passt. Wie erwartet ist die Programmausgabe:
arbeiteSchwer 2
So jetzt noch eine Fangfrage! Was ist das Ergebnis des folgenden Methoden-Aufrufs:
arbeiteSchwer(5.0,5.0);
Hier sind beide Parameter vom Datentyp double
.
Merkst du es?
Leider haben wir keine Methode, bei der die Parameterliste aus zwei double
Variablen besteht, deshalb kann der Compiler nicht feststellen welche der arbeiteSchwer
Methoden gemeint ist und wirft einen Fehler.
An diesem Beispiel erkennen wir das kein automatisches Typecasting stattfindet. Obwohl der double
Wert 5.0
verlustfrei in einen Integer-Wert konvertiert werden könnte, wird 5.0
weiterhin als double
Wert verarbeitet.
Um das Programm auch für diesen Aufruf ausführbar zu machen, müssen wir eine weitere Version der Methode arbeiteSchwer
hinzufügen, die zwei double
Werte verarbeiten kann.
public static void arbeiteSchwer(double x,double y){ System.out.println("arbeiteSchwer 4"); }
Hiermit ist unser Programm ausführbar und liefert die Ausgabe:
arbeiteSchwer 4
Fein! Schauen wir uns als nächstes das Überschreiben von Methoden an.
Das Überschreiben von Methoden
Beim Überladen einer Methode betrachten wir gleichnamige Methoden innerhalb einer einzigen Klasse.
Aber was machen wir mit gleichnamigen Methoden, die sich innerhalb einer Vererbungshierarchie auf verschiedene Klassen verteilen?
Genau dieses Problem löst das Überschreiben von Methoden.
Beginnen wir mit einem Beispiel und schreiben eine Klasse Vierbeiner
.
1: public class Vierbeiner { 2: protected String name = null; 3: Vierbeiner(String name){ 4: this.name = name; 5: } 6: public void myName(){ 7: System.out.println("Mein Name ist: "+this.name); 8: } 9: }
Diese Klasse besitzt ein Attribut name
, das wir mit Hilfe des Konstruktors initialisieren und mittels der Methode myName()
auf dem Bildschirm ausgeben können.
Sinnvollerweise leiten wir von dieser Klasse einen Hund ab.
1: public class Hund extends Vierbeiner{ 2: Hund(String name){ 3: super(name); 4: } 5: }
Die Klasse Hund
besitzt lediglich einen Konstruktor. Da wir nur von einer konstruierten Oberklasse ableiten können, müssen wir in Zeile drei mittels des Schlüsselwortes super
den Konstruktor von Vierbeiner
aufrufen.
Okay, hiermit können wir jetzt ein Hund-Objekt mit dem Namen Bello erzeugen.
Hund hund = new Hund("Bello");
Was passiert, wenn wir versuchen die Methode myName
aufzurufen?
hund.myName();
Weil hund
eine Instanz der Klasse Hund
ist, sucht der Compiler zuerst in dieser Klasse nach einer Methode mit dem Namen myName
und einer leeren Parameterliste.
Da in der Klasse Hund
eine solche Methode nicht existiert, wird die Suche in der Oberklasse Vierbeiner
fortgesetzt, in welcher der Compiler schließlich fündig wird und die Methode myName()
ausführt.
Die Ausgabe unseres Programms ist:
Mein Name ist: Bello
Was denkst du passiert, wenn wir der Klasse Hund
ebenfalls um die Methode myName
erweitern?
public class Hund extends Vierbeiner{ public void myName(){ System.out.println("Ich bin ein Hund und mein Name ist "+name+"!"); } Hund(String name){ super(name); } }
Welche Programmausgabe haben wir jetzt zu erwarten, wenn wir die Methode myName
aufrufen?
hund.myName();
Da hund
eine Instanz der Klasse Hund
ist, macht sich der Compiler wieder zuerst in der Klasse Hund
auf die Suche nach einer Methode mit dem Namen myName
und einer leeren Parameterliste.
Diesmal wird er fündig und führt die Methode myName()
aus der Klasse Hund
aus.
Die Programmausgabe lautet:
Ich bin ein Hund und mein Name ist Bello!
Und genau dieses Phänomen nennen wir Methoden Überschreibung.
Genau wie beim Überladen´ist auch beim Überschreiben von Methoden die Parameterliste das ausschlaggebende Kriterium für die eindeutige Zuordnung eines Aufrufs zu einer Methode.
Moment mal! Hat die Methode myName()
in der Klasse Vierbeiner jetzt überhaupt noch eine Funktion?
Methoden verketten
In unserem Beispiel haben wir die Methode myName()
aus der Klasse Vierbeiner
, für Instanzen eines Hundes, vollständig durch die gleichnamige Methode aus der Klasse Hund
abgelöst.
Häufig möchte man eine Methode aus einer Oberklasse aber lediglich verfeinern.
Und auch das ist möglich! Diesen Vorgang nennen wir Verkettung.
Am besten schauen wir uns auch das wieder anhand eines Beispiels an:
Wir erweitern die Klasse Vierbeiner
um eine fressen Methode.
public void fressen(){ System.out.println("Ich habe hunger!"); }
Wirklich nichts besonderes! Die Methode gibt lediglich eine Ausgabe auf dem Bildschirm aus.
Natürlich bekommt auch unser Hund regelmäßig Hunger, was er genau wie jeder andere Vierbeiner kundtut. Allerdings anders wie z.B. ein Elefant verlangt unser Bello zusätzlich einen Fressnapf, daher müssen wir die fressen()
Methode entsprechend erweitern.
Die naheliegendste Möglichkeit dies zu bewerkstelligen, ist die fressen()
Methode aus der Vierbeiner
Klasse zu kopieren, in die Klasse Hund
einzufügen und um eine entsprechende Bildschirmausgabe zu ergänzen.
public void fressen(){ System.out.println("Ich habe hunger!"); System.out.println("Bitte bringe mir einen Napf!"); }
Hierdurch haben wir die fressen()
Methode aus der Vierbeiner
Klasse mit einer neuen erweiterten Methode überschrieben.
Allerdings hat diese Vorgehensweise einen entscheidenden Nachteil.
Wir erzeugen redundanten Programmcode, was wir gerade in der objektorientieren Programmierung unbedingt vermeiden wollen.
Unser Ziel ist es, einmal geschriebenen Programmcode wiederverwenden zu können. In unserem Beispiel mag das vielleicht ein wenig kleinlich erscheinen, aber das liegt daran das wir hier ein Spielzeug-Problem betrachten.
In der Realität führt redundanter Programmcode zu schlecht wartbarer Software, da Änderungen an allen Stellen, an denen sich der kopierte Code befindet, durchgeführt werden müssen.
Besser wäre es doch, wenn wir die Ausgabe „Ich habe hunger!“ von der fressen()
Methode aus der Oberklasse Vierbeiner
erzeugen ließen.
Und hier hilft uns das Java Schlüsselwort super
weiter.
Aber zunächst eine Warnung! Um die fressen()
Methode aus der Oberklasse aufzurufen könnte man auf folgende Idee kommen.
1: public void fressen(){ 2: fressen(); 3: System.out.println("Bitte bringe mir einen Napf!"); 4:}
Das ist jedoch völlig falsch!
In Zeile Zwei wird nämlich nicht die fressen()
Methode aus der Oberklasse Vierbeiner
aufgerufen sondern es finden unendlich viele rekursive Aufrufe in der Klasse Hund
statt was zu einem Programmabsturz führt.
Achso, falls du den Begriff rekursiv noch nicht kennst. Rekursion bedeutet das sich eine Funktion selbst aufruft.
Das Schlüsselwort super
Mit Hilfe von super
können wir auf Methoden der Oberklasse zugreifen.
Ändern wir die Methode in der Klasse Hund
wie folgt:
public void fressen(){ super.fressen(); System.out.println("Bitte bringe mir einen Napf!"); }
Durch voranstellen des Schlüsselwortes super
(Zeile 2) erklären wir dem Compiler, dass die fressen Methode aus der Oberklasse gemeint ist.
Die Programmausgabe ist wie gewünscht:
Ich habe hunger! Bitte bringe mir einen Napf!
Hierbei wird die erste Ausgabe durch die fressen()
Methode der Klasse Vierbeiner
und die zweite durch die gleichnamige Methode in der Klasse Hund
erzeugt.
Was ist der Mehrwert vom Überladen und Überschreiben
Die Möglichkeit Methoden zu überladen und zu überschreiben bietet uns eine einfache Möglichkeit Klassen zu erweitern.
Mit Hilfe von überladenen Methoden gelingt es uns leicht die selbe Funktionalität für unterschiedliche Daten zur Verfügung zu stellen, denn auschlaggebend für die Zuordnung von überladenen Methoden ist die Parameterliste.
Gerade das Ändern von Parameterlisten führt bei Projekten immer wieder zu schwerwiegenden Fehlern, da Methoden-Aufrufe nicht mehr zu den tatsächlichen Definitionen der Methoden passen.
Das Überladen von Methoden bewahrt uns davor die Parameterliste von bereits existierenden Methoden modifizieren zu müssen.
Bereits existierende Methoden können unberührt bleiben, sodass bereits vorhandene Methodenaufrufe weiterhin funktionieren. Erweiterungen können wir in vollständig neuen Methoden mit gleichem Namen und angepasster Parameterliste implementieren.
Durch das überschreiben von Methoden haben wir zum einen die Möglichkeit eine Funktionalität, die eine Oberklasse zur Verfügung stellt abzulösen und zum anderen mit Hilfe des Schlüsselwortes super
zu erweitern.
Ich freue mich auf deine Fragen im Kommentarbereich!
Hat dir der Artikel gefallen? Dann folge uns doch am besten gleich auf Facebook!
ANIL
12. Februar 2017 at 15:29Danke
Kim Peter
13. Februar 2017 at 6:41Sehr gerne!
BumbleBuu
13. Februar 2017 at 14:20Das war um einiges Verständlicher als das Skript was wir in der Uni bekommen haben!! Vielen Dank für die tolle und vor allem einfache und verständliche Erklärung 😉
Kim Peter
13. Februar 2017 at 15:30Super! Vielen Dank für das Feedback.
Julia
23. Februar 2017 at 20:30Hallo. Ich habe eine andere Frage. Stellen wir vor, dass Vierbeiner extends Tier. Wenn ich in die Klasse Hund ein super.fressen() aufrufe, wird die aus der Klasse Vierbeiner oder Tier gerufen?
Danke für die Erklärung 🙂
Kim Peter
23. Februar 2017 at 22:18Hallo Julia, sehr gute Frage! Der Aufruf super.fressen() durchsucht deine Klassen-Hierachie von unten nach oben. D.h rufst du super.fressen innerhalb der Hunde Klasse auf und gibt es eine fressen Methode in der Vierbeiner-Klasse, dann wird diese aufgerufen und die fressen Methode aus Klasse Tier bleibt unberührt. Entfernst du allerdings die fressen Methode aus der Vierbeiner-Klasse, dann wird die Methode fressen aus der Tierklasse aufgerufen. Viele Grüße Kim
haunted
10. Mai 2017 at 6:41Vielen Dank! Sehr schöne Erklärungen und vor allen verständlich.
Kim Peter
10. Mai 2017 at 7:21Ich danke dir! 🙂
Elena
6. Juli 2017 at 13:02Sehr gut erklärt! Vielen Dank!
Kim Peter
7. Juli 2017 at 10:34Ich danke dir!
Felix
18. Juli 2017 at 14:08Vielen Dank für deine leicht verständliche Erklärung. Bitte mehr davon !
Kim Peter
19. Juli 2017 at 10:53Hallo Felix, vielen Dank! Hast du ein Themenwunsch? Viele Grüße Kim
Michelle
4. September 2017 at 11:40Du solltest Lehrer werden, weil du kannst das so erklären, dass man es auch versteht!
Kim Peter
4. September 2017 at 14:58Hallo Michelle, ich danke dir! Allerdings werde ich glaube beim Programmieren bleiben. Viele Grüße Kim
Rika
9. September 2017 at 11:48Das war sehr verständlich und hat mir beim lernen für die Klausur sehr geholfen. 🙂
Kim Peter
11. September 2017 at 12:17Danke! Ich wünsche dir viel Erfolg für die Klausur. Viele Grüße Kim
Julia
17. Oktober 2017 at 14:02Super einfach geschrieben und toll erklärt, danke dir!
Weiterhin viel Erfolg!
Kim Peter
17. Oktober 2017 at 15:41Hallo Julia, ich danke dir! Viele Grüße Kim
Andreas
8. März 2018 at 6:02Lieber Kim, danke für Deine Erklärungen, die ich gleich ausprobiert habe.
Die Fehlermeldungen habe ich auch behoben durch die fehl. Ergänzung in der Class Vierbeiner (reiner Anfängerfehler 🙂 ):
public static void main (String [] args) {
Hund hund = new Hund(„Bello“);
hund.myName();
}
VG Andreas
Kim Peter
8. März 2018 at 6:46Hallo Andreas, vielen Dank für deine Rückmeldung! Welche Fehlermeldung hast du denn bekommen? Viele Grüße Kim
Alex
18. August 2018 at 15:49Danke top erklärt!
Kim Peter
8. September 2018 at 19:31Hallo Alex, danke dir!
Jakob
21. Januar 2019 at 23:51Die Klausuren an der Uni stehen an… Rettung naht mit deiner Homepage. DANKE!!!
Kim Peter
22. Januar 2019 at 7:23Hallo Jakob, wünsche dir viel Erfolg bei den Klausuren! Viele Grüße Kim
Ute
26. April 2019 at 15:52Hallo Kim!
Frage: Ich habe zwei Unterklassen Festangestellter und Aushilfskraft. Beide Unterklassen haben eine Methode double berechneBrutto(int x), wobei x in beiden Unterklassen eine andere Übergabevariable ist und der Inhalt der Methode berechneBrutto ist auch unterschiedlich. Nun möchte ich die beiden Unterklassen in der Oberklasse Mitarbeiter zusammenfassen. Würde ich berechneBrutto in diesem Fall in beiden Unterklassen belassen oder in der Oberklasse eine irgendwie geartete Methode berechneBrutto vorsehen?
Kim Peter
28. April 2019 at 16:38Hallo Ute, wenn ich es richtig verstehe ist Mitarbeiter eine Klasse, die immer eine bestimmte Ausprägun hat (Festangestellter, Aushilfskraft). Daher würde ich Mitarbeiter als abstrakte Klasse und berechneBrutto als abstrakte Methode definieren. Viele Grüße Kim
Notgoingtohappen
16. März 2020 at 9:07Ehrenmann.
Mein Prof hat fast 45min versucht das zu erklären, wieso einfach wenns auch kompliziert geht?
Besten dank für den tollen artikel
Kim Peter
25. März 2020 at 7:54Freue mich wenn ich helfen kann.
Choose a style: