Der Begriff "Polymorphie" ist selbst polymorph. Es gibt nämlich zwei grundlegend verschiedene Bedeutungen desselben Begriffs. Daher unterscheidet man zwischen statischer Polymorphie und dynamischer Polymorphie.
Statische Polymorphie
Unter statischer Polymorphie versteht man die Überladung von Methoden oder Konstruktoren, also die Mehrfachverwendung desselben Namens mit unterschiedlichen Parameterlisten.
Der Konstruktor einer Klasse Kreis kann zum Beispiel so aussehen:
public Kreis()
public Kreis(int x, int y)
public Kreis(int x, int y, int radius)
Auch normale Methoden können in mehreren Varianten existieren, solange sich ihre Parameterlisten unterscheiden (Anzahl, Typ oder Reihenfolge der Parameter).
Der Compiler entscheidet bereits beim Übersetzen, welche der vorhandenen Methoden oder Konstruktoren dem konkreten Aufruf entspricht. Existiert eine Methode mit der aufgerufenen Parameterliste nicht, so gibt es eine entsprechende Fehlermeldung.
Dynamische Polymorphie
Definition
Polymorphie
Polymorphie (aus dem Griechischen „Vielgestaltigkeit“) bezeichnet in der objektorientierten Programmierung die Fähigkeit von Objekten unterschiedlicher Klassen, über eine gemeinsame Schnittstelle (z. B. eine geerbte Methode) auf gleiche Weise angesprochen zu werden, wobei die tatsächlich ausgeführte Methode von der konkreten Objektklasse abhängt.
Ein Beispiel
Dieses Quelltext-Beispiel stammt aus dem Buch von Lahres et. al [1]. Die abstrakte Oberklasse Geschaeftspartner besitzt zwei Unterklassen:Person und Organisation.
Die Klasse Person hat u.a. die beiden Instanzvariablen vorname und nachname, während die Klasse Organisation die Instanzvariable bezeichnung besitzt.
In einer weiteren Klasse Anzeige wird nun eine Methode anrede() implementiert:

Quelltext der Klasse Anzeige
Diese Methode kann nun mit beliebigen Objekten einer der beiden Unterklassen Person oder Organisation aufgerufen werden, wie das folgende Testprogramm zeigt:

Quelltext der Klasse Test
In Zeile 9 wird ein Objekt der Klasse Person erzeugt.
In Zeile 10 wird die Methode anrede() des Anzeige-Objektes brief aufgerufen; das Person-Objekt pers wird dabei als Parameter übergeben. Der println()-Befehl schreibt dann "Robert Martin" auf der Konsole aus, wobei der String aus den beiden Instanzvariablen vorname und name des Objektes pers zusammengesetzt ist.
Schauen wir uns dazu die Klasse Person näher an:

Quelltext der Klasse Person
Die Annotation @Override zeigt uns, dass die Methode anrede() bereits als abstrakte Methode in der Oberklasse Geschaeftspartner existiert und von allen Unterklassen implementiert werden muss.
Wird also die Methode anrede() eines Person-Objektes aufgerufen, wird ein String aus den beiden Instanzvariablen vorname und name generiert und zurückgegeben.
Wird anrede() dagegen für ein Organisation-Objekt aufgerufen, liefert die Methode nur den Wert der Instanzvariablen bezeichnung zurück:

Quelltext der Klasse Organisation
Beide Unterklassen überschreiben also dieselbe abstrakte Methode anrede(), aber mit unterschiedlicher Implementierung.
Die Klasse Anzeige kann dennoch einheitlich p.anrede() aufrufen – das ist Polymorphie: Die Auswahl der konkreten Methode erfolgt zur Laufzeit in Abhängigkeit vom tatsächlichen Objekttyp.
Da Geschaeftspartner abstrakt ist, kann kein Objekt dieser Klasse direkt erzeugt werden. Der Parameter p in Anzeige.anrede() ist daher stets ein Objekt einer konkreten Unterklasse. Die Angabe des Typs Geschaeftspartner dient hier als allgemeiner Platzhalter für beliebige Unterklassenobjekte.
Quellen:
- Lahres et al.: Objektorientierte Programmierung, Rheinwerk Computing 2021.
- Barnes, Kölling: Java lernen mit BlueJ - Objects first. Pearson-Verlag 2019.
- Ullenboom: Java ist auch eine Insel, Rheinwerk Computing 2023.