Jetzt anmelden...

Login
Passwort
Registrieren

Java Tutorial #31

Polymorphie in Java

Polymorphie erlaubt es uns, sehr flexiblen und mächtigen Code zu schreiben. Die Regeln der Polymorphie zu beherrschen ist ein essential skill für jeden Java-Entwickler.

Kommentare [1]

Stefan 10.06.2019

Infos zum Artikel

Kategorie Java
Autor Stefan
Datum 10.06.2019

Thema in Kurzform

  • Variablen in Java sind polymorph ("verschiedenartig"). Das bedeutet, dass eine Variable auf ein Objekt verweisen kann, dessen Typ entweder gleich dem deklarierten Typ der Referenzvariablen oder ein beliebiger Subtyp davon ist:
Auto a1 = new Auto();
Auto a2 = new Cabrio();

Polymorphe Variablen und Subtyping

Variablen für Objekttypen (Referenzvariablen) sind in Java polymorph. "Polymorph" heißt so viel wie "verschiedenartig". Für die objektorientierte Programmierung bedeutet dies, dass eine Variable auf verschiedene Objekttypen verweisen kann - auf welche genau, darüber gibt folgende Regel Auskunft:

  • Eine Variable kann auf ein Objekt verweisen, dessen Typ entweder gleich dem deklarierten Typ der Referenzvariablen oder ein beliebiger Subtyp davon ist.

Lassen Sie das erst mal sacken und genießen Sie Ihren Kaffee :-)

Um uns der polymorphen Grundeigenschaft von Referenzvariablen besser zu verstehen, sehen wir uns ein kleines Beispiel an:

Java Polymorphie

Im Vererbungsbaum erkennen wir, dass die Klassen Cabrio und Limousine von der Oberklasse Auto erben. Die Klasse Auto sollte in einem guten Programm als abstract definiert sein; zur besseren Veranschaulichung lassen wir sie aber ausnahmsweise konkret.

Nutzen wir nun die polymorphe Eigenschaft von Variablen und weisen den Referenzen vom Typ Auto verschiedene Objekttypen zu:

Auto a1 = new Auto();
Auto a2 = new Cabrio();
Auto a3 = new Limousine();

In Zeile 1 haben wir ein gewohntes Bild: Referenztyp und Objekttyp sind gleich. Dies gilt jedoch nicht mehr für Zeile 2 und 3: Hier sind die Objekte nämlich Subtypen von Auto.

Java Polymorphie

Nochmal: Wir können Variablen nur Objekte gleichen Typs oder Objekte eines passenden Subtyps zuweisen. Deshalb ist folgende Zuweisung nicht erlaubt:

Cabrio a = new Auto(); // ERROR!

Wir können Referenzvariablen von Subtypen (Cabrio) also niemals Objekte von Supertypen (Auto) zuweisen. Polymorphie funktioniert also nur von oben nach unten und das ist auch gut so. Alles andere wäre Wahnsinn!

Der Vorteil der Polymorphie

Durch Polymorphie müssen wir unseren Code nicht ändern, wenn unser Programm neue Unterklassen erhält. Polymorphie erlaubt es uns außerdem, sehr flexiblen und mächtigen Code zu schreiben. Wir sehen uns hierzu zwei Beispiele an, wie sie häufig vorkommen.

Polymorphie bei Methodenparametern

Nehmen wir einmal an, wir hätten eine Autowerkstatt (repäsentiert durch die Klasse Werkstatt). Natürlich sollen dort verschiedene Autotypen repariert werden (repräsentiert durch eine statische Methode namens reparieren()). Da in Java auch Parameter-Variablen für Objekttypen polymorph sind, können wir folgenden eleganten Code schreiben:

public class Werkstatt {

    public static void reparieren(Auto a){
        //
    }

    public static void main(String[] args){

        Auto a1 = new Cabrio();
        Limousine a2 = new Limousine();
        Cabrio a3 = new Cabrio();

        reparieren(a1);
        reparieren(a2);
        reparieren(a3);
    }
}

Wir wir sehen, ist die Parameter-Variable der Methode reparieren() mit dem Typ Auto deklariert. Dadurch, dass der Parameter polymorph ist, können neben Auto auch Cabrio und Limousine übergeben werden.

Polymorphie bei Sammlungen

Polymorphie können wir auch bei der Arbeit mit Listen bzw. Sammlungsklassen (wie z.B. der ArrayList) voll ausnutzen. Nehmen wir zum Beispiel an, wir führen eine Liste verschiedener Autos. Auch hier soll die Sammlung ganz unterschiedliche Autotypen aufnehmen können - und tatsächlich ist das problemlos möglich. Wir müssen die ArrayList lediglich anweisen, alle Typen von Auto anzunehmen (Typ in spitze Klammern schreiben):

Auto a1 = new Cabrio();
Limousine a2 = new Limousine();
Cabrio a3 = new Cabrio();

ArrayList<Auto> liste = new ArrayList()<>;
liste.add(a1);
liste.add(a2);
liste.add(a3);

Polymorphie bei Methoden

Wenn es bei Polymorphie um Methoden geht, müssen wir noch einen Schritt weiter denken:

Java Polymorphie

Die Klasse Cabrio besitzt zwei Methoden, nämlich die von Auto geerbte Methode fahren() und die ihr eigene Methode verdeckOeffnen().

Was denken Sie passiert, wenn wir folgenden Code ausführen?

Auto a1 = new Cabrio();
a1.verdeckOeffnen(); // error

Die Antwort ist, dass dieser Code einen bösen Compiler-Fehler erzeugt, nämlich: cannot resolve method 'verdeckOeffnen()'.

Die Methode verdeckOeffnen() kann also nicht ausgeführt werden. Wir sind ein wenig verwundert, dass der Methodenaufruf nicht funktioniert, da doch die Klasse Cabrio die konkrete Methode verdeckOeffnen() besitzt. Was ist der Grund?

Wenn wir Methoden aufrufen, müssen wir im Zusammenhang mit der Polymorphie folgende Regel beachten:

  • Wenn wir auf einer Referenz eine Methode aufrufen wollen, muss diese in der Klasse des Referenztyps vorhanden sein.

Der Methodenaufruf hat also deshalb nicht funktioniert, da der Referenztyp von a1 eben Auto ist - und in der Klasse Auto gibt es keine Methode verdeckOeffnen() (wäre die Variable a1 vom Referenztyp Cabrio, hätte es funktioniert).

Der tiefere Grund für dieses Verhalten liegt darin, dass Java eine streng typisierte Sprache ist. Da Objekte erst zur Laufzeit erstellt werden (also nach erfolgreichem Compilieren und Programmstart), kann Java zuvor unmöglich wissen, welche Methoden der Objekttyp hat. Es muss daher zur Laufzeit des Programms sichergestellt sein, dass Methodenaufrufe auch wirklich funktionieren. Damit dies garantiert ist, geht Java beim Referenzieren auf ein Objekt davon aus, dass der Objekttyp dem Referenztyp entspricht.

Mit anderen Worten: Wenn unsere Referenz vom Typ Auto ist, können wir zugewiesene Objekte auch nur wie ein Auto behandeln - und Auto kennt keine Methode verdeckOeffnen().

Das "Problem" der Polymorphie

Wenn wir die Polymorphie bei Parameter-Referenzen oder Listen einsetzen, müssen wir immer bedenken, dass wir nur diejenigen Methoden ausführen können, die dem deklarierten Typ entsprechen. Das heißt:

public static void meineMethode(Auto a){
    a.verdeckOeffnen(); // geht nicht!
}

public static void main(String[] args){
    Auto a1 = new Cabrio();
    meineMethode(a1);
}

Außerdem:

Auto a1 = new Limousine();
Auto a2 = new Cabrio();

ArrayList<Auto> liste = new ArrayList()<>;
autos.get(0).verdeckOeffnen(); // geht sowieso nicht!
autos.get(1).verdeckOeffnen(); // geht nicht!

Wenn wir mit der ArrayList-Methode get() ein Objekt der Sammlung zurückliefern, geht Java davon aus, dass es sich um ein Objekt vom Typ Auto handelt, da die ArrayList ja mit ArrayList<Auto> deklariert wurde.

Das ist doch bescheuert, oder!? Jetzt machen wir uns die Mühe und lernen, Polymorphie in Methoden und Listen einzusetzen, und was passiert? Wir können auf unseren Objekten bestimmte Methoden garnicht mehr ausführen. Was nutzt ein Cabrio, wenn wir die Methode verdeckOeffnen() nicht ausführen können?

Tatsächlich gibt es eine Technik, mit der wir das wieder hinbekommen: Die Typumwandlung (Casten). Dies aber ist ein eigenes Thema und das heben wir uns für das nächste Kapitel auf 🤗

Übungen

einfach

Betrachten Sie folgende Klassen:

class Animal{ }

class Fish extends Animal{ }

class Shark extends Fish{ }

Welche der folgenden Zuweisungen ist nicht korrekt (eine Auswahl)?

Animal a = new Animal();
Animal b = new Fish();
Animal c = new Shark();
Fish d = new Shark();
Shark e = new Fish();
Lösung ein-/ausblenden

mittel

Java Polymorphie

Angesichts dieser Vererbungshierarchie: Ist der folgende Code korrekt?

Husky h = new Husky();
Hund h2 = h;
h2.bellen();
h2.schlittenZiehen();
Lösung ein-/ausblenden

schwer

Der folgende Code erzeugt einen Compiler-Fehler. Wo tritt dieser auf?

class A{
    public static void main(String args[]){
        A x = new A(); // 1
        B y = new B(); // 2
        C z = new C(); // 3
        x = y; // 5
        y = x; // 6
        z = x; // 7
    }
}

class B extends A { }
class C extends B { }
Lösung ein-/ausblenden

Kommentar schreiben

Nur angemeldete und aktivierte Benutzer können kommentieren.

Alle Kommentare

Jan
23.09.2019 - 11:39
Wäre eine bessere Übersetzung für polymorph nicht "vielgestaltig"? Sehe diese Übersetzung ("verschiedenartig") zum ersten Mal und finde sie verwirrend, kann aber auch sein, dass es nur mir so geht. :-)

Statische Elemente

Statische Variablen und Methoden nutzen

Vererbung einfach erklärt

Lernen Sie die Grundlagen der Vererbung in Java

Verzweigungen in Java

Eine zentrale Notwendigkeit der Programmierung sind Verzweigungen.

FALCONBYTE.NET

Handmade with 🖤️

© 2018, 2019 Stefan E. Heller

Impressum | Datenschutz

facebook programmieren lernen twitter programmieren lernen youtube programmieren lernen