Java Methoden überschreiben oder überladen. Was ist der Unterschied? Wie geht es?

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:

  1. Methodenname
  2. Parameterliste
  3. 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.

Java Methoden überladen

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

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!

 

Hallo ich bin Kim und ich möchte ein großer Programmierer werden. Machst du mit?

Kommentare (28)

  • Antworte

    Danke

    • Sehr gerne!

  • Antworte

    Das 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 😉

    • Super! Vielen Dank für das Feedback.

  • Antworte

    Hallo. 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 🙂

    • Hallo 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

  • Antworte

    Vielen Dank! Sehr schöne Erklärungen und vor allen verständlich.

    • Ich danke dir! 🙂

  • Antworte

    Sehr gut erklärt! Vielen Dank!

    • Ich danke dir!

  • Antworte

    Vielen Dank für deine leicht verständliche Erklärung. Bitte mehr davon !

    • Hallo Felix, vielen Dank! Hast du ein Themenwunsch? Viele Grüße Kim

  • Antworte

    Du solltest Lehrer werden, weil du kannst das so erklären, dass man es auch versteht!

    • Hallo Michelle, ich danke dir! Allerdings werde ich glaube beim Programmieren bleiben. Viele Grüße Kim

  • Antworte

    Das war sehr verständlich und hat mir beim lernen für die Klausur sehr geholfen. 🙂

    • Danke! Ich wünsche dir viel Erfolg für die Klausur. Viele Grüße Kim

  • Antworte

    Super einfach geschrieben und toll erklärt, danke dir!
    Weiterhin viel Erfolg!

    • Hallo Julia, ich danke dir! Viele Grüße Kim

  • Antworte

    Lieber 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

    • Hallo Andreas, vielen Dank für deine Rückmeldung! Welche Fehlermeldung hast du denn bekommen? Viele Grüße Kim

  • Antworte

    Danke top erklärt!

    • Hallo Alex, danke dir!

  • Antworte

    Die Klausuren an der Uni stehen an… Rettung naht mit deiner Homepage. DANKE!!!

    • Hallo Jakob, wünsche dir viel Erfolg bei den Klausuren! Viele Grüße Kim

  • Antworte

    Hallo 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?

    • Hallo 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

  • Antworte

    Ehrenmann.
    Mein Prof hat fast 45min versucht das zu erklären, wieso einfach wenns auch kompliziert geht?

    Besten dank für den tollen artikel

    • Freue mich wenn ich helfen kann.

Antworte auf haunted Abbruch