Jetzt anmelden...

Login
Passwort
Registrieren

Java Tutorial #32

Typumwandlung (Casting) in Java

Wer Polymorphie in Java sinnvoll einsetzen will, kommt um das Thema Typumwandlung nicht herum. Wir bringen Ihnen in diesem Kapitel das Wichtigste dazu bei. Außerdem erfahren Sie, was genau hier eigentlich umgewandelt werden soll.

Kommentare [0]

Stefan 10.06.2019

Infos zum Artikel

Kategorie Java
Autor Stefan
Datum 10.06.2019

Thema in Kurzform

  • Problem: Wenn wir auf einer Referenz eine Methode aufrufen wollen, muss diese in der Klasse des Referenztyps vorhanden sein:
Hund h = new Husky();
h.schlittenZiehen(); // klappt nicht, Klasse Hund kennt die Methode nicht
  • Lösung: Casting der Referenz in den Typ des Objekts:
Husky hx = (Husky) h; // Typumwandlung
hx.schlittenZiehen(); // ok!

Das Problem

Wie wir im letzten Kapitel gesehen haben, können wir auf Objektreferenzen nur die Methoden ausführen, die im Referenztyp auch vorhanden sind.

Nochmal ein kleines Beispiel hierzu:

class Hund {
    public void bellen(){}
}

class Husky extends Hund {
    public void schlittenZiehen(){}
}

Nun erzeugen wir ein neues Husky-Objekt und weisen es einer Referenz vom Typ Hund zu. Anschließend wollen wir auf der Hund-Referenz die Methode schlittenZiehen() aufrufen, denn ein Husky sollte doch eigentlich einen Schlitten ziehen können, oder? 😉

Hund h = new Husky();
h.schlittenZiehen(); // ERROR

Nein, es funktioniert nicht! Der Code erzeugt einen Fehler, da die Methode schlittenZiehen() nicht ausgeführt werden kann: cannot resolve method 'verdeckOeffnen().

Auch wenn wir eindeutig wissen, dass h auf ein Objekt vom Typ Husky zeigt, interessiert das den Compileraber einen Scheiß! Er prüft lediglich den Referenztyp und lässt nur diejenigen Methoden ausführen, die dort auch vorhanden sind. Da der Referenztyp von h nun eben Hund ist und dort keine Methode schlittenZiehen() definiert ist, kann sie auch nicht ausgeführt werden - ganz egal, auf welchen Objekttyp gezeigt wird.

Dies zu wissen wird besonders bei der Arbeit mit Sammlungsklassen (z.B. ArrayList) wichtig:

Husky h1 = new Husky();
Schaeferhund h2 = new Schaeferhund();
Mops h3 = new Mops();

ArrayList<Hund> hunde = new ArrayList<>();
hunde.add(h1);
hunde.add(h2);
hunde.add(h3);

hunde.get(0).schlittenZiehen(); //  error
hunde.get(1).bewachen(); //  error
hunde.get(2).mopsMethode(); //  error

Wir erstellen zunächst drei Objekte von Hund-Subtypen und adden sie in eine ArrayList<Hund>. Anschließend rufen wir die drei Elemente der Sammlung mittels get auf und wollen die speziellen Methoden von Husky, Schaeferhund und Mops ausführen.

Dies funktioniert aber nicht! Der Grund ist, dass aus einer ArrayList<Hund> ausschließlich Hund-Referenzen herauskommen - und da die Klasse Hund eben keine Methoden schlittenZiehen(), bewachen() und mopsMethode() hat, crasht der Code.

Die Lösung: Typumwandlung

Um also die Methoden eines Subtyps ausführen zu können, müssen wir die Referenz in eben diesen Subtyp "nach unten" umwandeln. Dieser Vorang wird Typumwandlung bzw. Casting (genauer: Downcasting) genannt.

Die Tymumwandlung nach "unten" kann nicht automatisch geschehen, sondern erfolgt durch den Programmierer explizit.

Was wir hierzu benötigen ist der Cast-Operator. Er besteht aus zwei runden Klammern und darin der Name des Zieltyps. Zum Beispiel:

Hund h = new Husky();
Husky hx = (Husky) h;

Wir haben nun eine neue Referenz hx vom Typ Husky, dass auf das Husky-Objekt verweist. Durch hx haben wir jetzt Zugriff auf die Methoden der Klasse Husky erhalten!

Auf das Objekt selbst hat dies keinerlei Auswirkung - es geht um die Referenzen! Deshalb ist es auch nicht richtig, wenn gelegentlich von "Objekt-Umwandlung" die Rede ist.

Java Casten

x.schlittenZiehen(); // ☠ error!
hx.schlittenZiehen(); // ok!

Regeln fürs Casten

Allerdings dürfen wir nicht wild durch die Gegend casten - es gibt eine strenge Regel:

  • Es dürfen nur Typen gecasted werden, die eine Vererbungsbeziehung zueinander haben.

Sehen wir uns folgende Vererbungshierarchie an, um das besser zu verstehen:

Java Casten

Keine Beziehung zueinander haben die drei Subtypen Husky, Schaeferhund und Mops. Wir können einen Husky deshalb niemals zu einem Schaeferhund oder Mops machen:

Husky h = new Husky();
Schaeferhund hx = (Schaeferhund) h; // error!

Dieser Code lässt sich nicht compilieren (inconvertible types). Die Husky-Referenz h kann ja niemals auf ein Schaeferhund-Objekt zeigen (sondern nur auf den Objekttyp Husky und alle Unterklassen davon). Das weiß der Compiler und blockt den Casting-Versuch sofort ab.

ClassCastException

Ein wenig anders sieht es hier aus:

Hund h = new Husky();
Schaeferhund h2 = (Schaeferhund) h;

Diesen Code lässt der Compiler durchgehen - ja er muss ihn durchgehen lassen, da er zum Zeitpunkt des Compilierens nicht wissen kann, welches Objekt der Hund-Referenz h zugewiesen wird (wir erinnern uns: Objekte werden erst zur Laufzeit erstellt).

Aber: Da h in unserem Beispiel auf ein Objekt vom Typ Husky zeigt, wird die Typumwandlung zur Laufzeit scheitern (ClassCastException) und das Programm wahrscheinlich abstürzen. Hätten wir es mit einem Schaeferhund-Objekt zu tun gehabt, wäre das Downcasting in Ordnung gewesen.

Upcasting

Das "nach oben" Umwandeln muss nicht explizit geschrieben werden:

Husky h = new Husky();
Hund hx = (Hund) h; // nicht notwendig, schlechter Stil

Das Husky-Objekt wird in der zweiten Zeile durch Upcasting einer Hund-Variable zugewiesen (der höhere Typ). Dieser Vorgang ist aber redundant und sollte vermieden werden. Es geht doch viel einfacher:

Husky h = new Husky();
Hund hx = h; // Polymorphie!

Da Java den höheren Typ automatisch casted und wir nichts weiter tun müssen, spricht man auch vom impliziten Casten. Wir dürfen aber nicht vergessen, dass auf hx die Husky-typischen Methoden nicht verfügbar sind.

Der instanceof-Operator

Wenn wir eine Typumwandlung vor einer ClassCastException absichern wollen, können wir vor dem Casten prüfen, ob ein gegebenes Objekt von einem bestimmten Typ ist. Der instanceof-Operator wir folgendermaßen eingesetzt

Referenzvariable instanceof MyClass

Dieser Test liefert zur Laufzeit true, wenn die Referenz auf eine Instanz der Klasse MyClass zeigt.

Hier ein konkreten Beispiel:

Hund h = new Husky();

if(h instanceof Schaeferhund){
    Schaeferhund h2 = (Schaeferhund) h;
}

Mit diesem Code kommen wir elegant um einen Crash zur Laufzeit herum. Da nämlich h eben keine Instanz von Schaeferhund ist (d.h. die Referenz h verweist nicht auf ein Schaeferhund-Objekt), wird die Typumwandlung nicht ausgeführt.

Beachten Sie aber, dass folgende Prüfung ebenfalls true ergibt:

h instanceof Hund // true

instanceof kann nämlich nicht nur den tatsächlichen Typ eines Objekts prüfen, sondern auch alle Supertypen davon. Da h auf ein Husky-Objekt zeigt - und Hund ein Supertyp davon ist - ergibt der Test true.

Entsprechend ist folgender Prüfung immer wahr:

referenz instanceof Object

Wir erinnern uns: Object ist die Superklassen aller Objekte in Java.

Übungen

einfach

Betrachten Sie folgende Klassen:

class Mars extends Planet{ }

class Earth extends Planet{ }

Ist der folgende Code zulässig?

Mars mars = new Mars();
Earth earth = (Earth) mars;
Lösung ein-/ausblenden

mittel

Was ist das Ergebnis des folgenden Codes (Earth und Mars erweitern Planet):

public static void main(String[] args){

    Planet p1 = new Earth();
    Planet p2 = new Mars();

    if(p1 instanceof Planet){
        System.out.println("good old blue");
    }
    else{
        Earth e = (Earth) p2;
    }
}

A. Compiler-Fehler: Planet p1 = new Earth(); ist unzulässig.
B. Der Code läuft fehlerfrei, es wird aber nichts ausgegeben.
C. Der Text good old blue erscheint auf der Konsole.
D. Der Text good old blue erscheint auf der Konsole, anschließend stürzt das Programm ab (ClassCastException)
E. Es wird nichtsauf der Konsole angezeigt und das Programm stürzt ab (ClassCastException)

Lösung ein-/ausblenden

schwer

Wie oft wird The red one! auf der Konsole ausgegeben?

public static void main(String[] args){

    Planet p = null;
    ArrayList<Planet> liste = new ArrayList<>();
    liste.add(new Mercury());
    liste.add(new Venus());
    liste.add(new Earth());
    liste.add(new Mars());
    liste.add(new Jupiter());

    for(Planet sV : liste){
        if(sV instanceof Mars){
            p = sV;
            liste.remove(sV);
        }
    }

    if(p instanceof Mars){
        System.out.println("The red one!");
    }

    for(Planet sV : liste){
        if(sV instanceof Mars){
            System.out.println("The red one!");
        }
    }

}
Lösung ein-/ausblenden

Kommentar schreiben

Nur angemeldete und aktivierte Benutzer können kommentieren.

Alle Kommentare

Es gibt bislang noch keine Kommentare zu diesem Thema.

toString() Methode

Lernen Sie hier, wie Sie die toString() Methode korrekt einsetzen

Arrays in Java

Arrays ermöglichen das Speichern von Daten in einer übergeordneten Datenstruktur

Schleifen in Java

Schleifenstrukturen gehören zu den wichtigsten Grundlagen in der Programmierung überhaupt.

FALCONBYTE.NET

Handmade with 🖤️

© 2018, 2019 Stefan E. Heller

Impressum | Datenschutz

facebook programmieren lernen twitter programmieren lernen youtube programmieren lernen