Was versteht man unter Polymorphie?

Polymorphie (gr. Vielgestaltigkeit) bezeichnet eine Sprachstruktur, nach der beim Zugriff auf Methoden mit identischer Signatur diese unterschiedliche Ergebnisse liefern. Das Verhalten ist insbesondere im Zusammenhang mit Vererbung anzutreffen.

Polymorphie stellt eines der zentralen Prinzipien in der objektorientierten Programmierung dar. Man unterscheidet zwischen statischer und dynamischer Polymorphie [LaRa]. Hier wird im Sinne der o.a. Charakterisierung besonders auf die dynamische Polymorphie eingegangen.

Statische Polymorphie

Statische Polymorphie zeigt sich in Form des Überladens von Methoden, bei dem sich mehrere Methodensignaturen nicht in ihrem Bezeichner, jedoch in deren Parameterliste unterscheiden. Die Wahl der auszuführenden Methode geschieht somit anhand von Anzahl und Typ der Methodenparameter.

public class Ueberladen {

    public static void main(String[] args) {
        Ueberladen u = new Ueberladen();
        u.print("Hallo");
        u.print("Moin", "Klaus");
    }
    
    private void print(String txt) {
        System.out.println(txt);
    }
    
    private void print(String txt, String name) {
        System.out.println(txt + " " + name);
    }
}

Dynamische Polymorphie

Bei der dynamischen Polymorphie werden in mehreren Klassen einer Vererbungslinie gleiche, d.h. mit identischen Signaturen versehene Methoden unterschiedlich implementiert, sodass zur Laufzeit entschieden werden muss, welche der Methoden ausgeführt wird. Dies ist dann der Fall, wenn die Kindklassen die Methoden der Elternklasse überschreiben (nicht überladen!). Es muss dann zur Laufzeit entscheiden werden, ob die Methode der Eltern- oder diejenige der Kindklasse ausgeführt wird.

public class Polymorphie {
    
    public Polymorphie() {
        Tier[] tiere = { new Vogel(), new Wurm() };
        for (int i = 0; i < tiere.length; i++) {
            tiere[i].bewegtSich();
            tiere[i].zwitscher();   // Fehler
            tiere[i].frisst();      // Fehler
        }
    }

    public static void main(String[] args) {
        new Polymorphie();
    }
}

class Tier {
    void bewegtSich() {
        System.out.println("Schwimmen, laufen, kriechen, hüpfen oder fliegen?");
    }
}

class Vogel extends Tier {
    @Override
    void bewegtSich() {
        System.out.println("Ich fliege");
    }
    
    void zwitscher() {
        System.out.println("tirilli");
    }
    
    void frisst() {
        System.out.println(getClass() + " frisst");
    }
}

class Wurm extends Tier {
    @Override
    void bewegtSich() {
        System.out.println("Ich krieche");
    }
    
    void frisst() {
        System.out.println(getClass() + " frisst");
    }
}

Das Beispiel zeigt vier Klassen:

  • Tier definiert die Methode bewegtSich(), in der ein einfacher Text ausgegeben wird.
  • Die Klassen Vogel und Wurm sind von Tier abgeleitet. Sie überschreiben jeweils die Methode bewegtSich() der Elternklasse Tier.
  • Polymorphie enthält main() und dient der Steuerung des Beispiels. Im Konstruktor wird ein Array vom Typ Tier erzeugt und mit je einem Vogel- und Wurm-Objekt beladen. Das Array wird durchlaufen und auf jedem enthaltenen Objekt die Methode bewegtSich() ausgeführt.

Es muss hier nochmals betont werden, dass das erwähnte Array vom Typ Tier ist und nicht etwa von einem der abgeleiteten Typen Wurm oder Vogel. Dies bewirkt, dass auch nur Fähigkeiten der Elternklasse ausgeführt werden können, wenn auch auf die in der jeweiligen Kindklasse definierten Art und Weise. Die polymorphen Methoden müssen also in der Elternklasse zwingend bereits deklariert worden sein.
Verständlich wird dies, wenn man erkennt, dass nicht nur die Ausführung der Methode zwitscher() der Kindklasse Vogel, sondern auch die Methode frisst(), die sogar in beiden Kindklassen enthalten ist, bei Ausführung jeweils einen Compilerfehler erzeugt.

Überschriebene Methoden sollte man mit der Annotation @Override kennzeichnen. Der Compiler erfährt hierdurch, dass eine Methode der Elternklasse spezifiziert wird. Das mag auf den ersten Blick überflüssig erscheinen, verhindert jedoch Fehler. Moderen Entwicklungsumgebungen "meckern", wenn eine Methode mit @Override gekennzeichnet ist, diese jedoch z.B. durch einen Schreibfehler im Bezeichner gar kein Überschreiben vornehmen. Umgekehrt kann es durchaus passieren, dass eine Methode überschrieben wird, ohne dass dies beabsichtigt ist. Gewöhnt man sich die Verwendung der Annotation konsequent an, so fällt dies bei der Quelltextlektüre durch das Fehlen von @Override auf.

Quellen

[LaRa] B.Lahres, G.Rayman, Objektorientierte Programmierung, Rheinwerk Computing, 2. aktualisierte und erweiterte Auflage, 2009