Im folgenden Beispiel sind die Klassen Rechteck
und Kreis
Unterklassen der Klasse Figur
. Letztere besitzt ein Attribut Farbe
und eine Methode schreibeDaten()
mit der grundlegenden Funktionalität, diese Farbe auszugeben. Es ist nicht sinnvoll, Objekte der Klasse Figur
zu instanzieren, daher haben wir die Klasse als abstract
deklariert.
abstract class Figur { String farbe; public Figur(String farbe) { this.farbe = farbe; } public void schreibeDaten() { println("Farbe: " + farbe); } }
Die Klasse Rechteck
ist eine Unterklasse der Klasse Figur
und besitzt über das Attribut Farbe
hinaus noch die Attribute Breite
und Länge
. Die Methode schreibeDaten()
der Klasse Figur ist für Rechteck-Objekte nicht mehr hinreichend, da bei diesen Objekten auch die Länge und die Breite ausgegeben werden soll.
Wir schreiben in die Klasse Rechteck
daher eine weitere Methode schreibeDaten()
mit derselben Signatur (d.h. demselben Bezeichner, denselben Parametertypen und demselben Rückgabedatentyp) wie die Methode der Oberklasse Figur. Man sagt: „Wir überschreiben die Methode schreibeDaten()
der Klasse Figur
in der Klasse Rechteck
.“
Natürlich soll auch bei Rechtecken neben der Länge und der Breite noch deren Farbe ausgegeben werden. Wir wollen daher in der Methode schreibeDaten()
der Klasse Rechteck
die gleichnamige Methode der Oberklasse Figur
aufrufen. Dazu benutzen wir - wie auch schon bei Konstruktoren - das Schlüsselwort super
.
Wir erinnern uns:
Innerhalb des Konstruktors einer Unterklasse ist der Aufruf des Konstruktors der Oberklasse mittels super
verpflichtend. Diese Pflicht besteht bei anderen überschriebenen Methoden nicht.
class Rechteck extends Figur { double breite; double höhe; public Rechteck(String farbe, double breite, double höhe) { super(farbe); this.breite = breite; this.höhe = höhe; } public void schreibeDaten() { super.schreibeDaten(); // Aufruf der gleichnamigen Methode der Oberklasse println("Breite: " + breite, Color.lightblue); println("Höhe: " + höhe, Color.lightblue); } }
Wird nun bei einem Rechteck
-Objekt die Methode schreibeDaten()
aufgerufen, so kommt immer die Methode der Klasse Rechteck
zum Zug, nicht die Methode der Oberklasse Figur
. Probiere es im folgenden Beispiel aus, indem Du das Programm im Einzelschrittmodus „step into ()“ ausführst!
Beachte die Methodenaufruf in den Zeilen 4 und 6 des Hauptprogramms oben:
f1.schreibeDaten(); println(); f2.schreibeDaten();
Die Variablen f1
und f2
sind beide vom selben Datentyp Figur
. Trotzdem bewirkt der Aufruf der Methode schreibeDaten()
jeweils etwas anderes. In Zeile 4 wird die Methode schreibeDaten()
der Klasse Rechteck
aufgerufen, in Zeile 6 die Methode schreibeDaten()
der Klasse Kreis
.
Diesen Sachverhalt nennt man Polymorphismus („Vielgestaltigkeit“, siehe Wikipedia), da der Aufruf mit demselben Methodenbezeichner die Ausführung verschiedener Methoden zur Folge hat.
Noch eindrucksvoller wirkt der Polymorphismus im folgenden Beispiel:
In Zeile 6 wird beim ersten Schleifendurchlauf die Methode schreibeDaten()
der Klasse Rechteck
aufgerufen, beim zweiten Durchlauf die Methode schreibeDaten()
der Klasse Kreis
.
Am Beispiel oben wird deutlich, dass der Computer erst zur Laufzeit bestimmt, welche der beiden Methoden aufgerufen wird. Man spricht von dynamic Binding.