„Hi, Amigo! Das Thema der heutigen Lektion sind erweiternde und einschränkende Typumwandlungen. Du hast schon vor langer Zeit erfahren, wie man primitive Datentypen erweitern und einschränken kann. In Level 10. Heute werden wir darüber sprechen, wie das bei Referenztypen, d.h. Instanzen von Klassen, funktioniert.“

Eigentlich ist das alles ganz einfach. Stelle dir die Vererbungskette einer Klasse vor: die Klasse, ihre Elternklasse, die Elternklasse der Elternklasse usw., bis zurück zur Object-Klasse. Da eine Klasse alle Member-Methoden der Klasse, von der sie erbt, enthält, kann eine Instanz der Klasse in einer Variable gespeichert werden, deren Tyo irgendeinem der Typen ihrer Elternklasse entspricht.

Hier ist ein Beispiel:

Code Beschreibung
class Animal
{
public void doAnimalActions();
}class Cat extends Animal
{
public void doCatActions();
}class Tiger extends Cat
{
public void doTigerActions();
}
Hier haben wir drei Klassendeklarationen: Animal, Cat und Tiger. Cat erbt von Animal. Und Tiger erbt von Cat.
public static void main(String[] args)
{
Tiger tiger = new Tiger();
Cat cat = new Tiger();
Animal animal = new Tiger();
Object obj = new Tiger();
}
Ein Tiger-Objekt kann immer einer Variablen zugewiesen werden, deren Typ dem eines seiner Vorfahren entspricht. Für die Klasse Tiger sind dies Cat, Animal und Object.

Nun schauen wir uns erweiternde und einschränkende Umwandlungen an.

Wenn eine Zuweisungsoperation dazu führt, dass wir in der Vererbungskette nach oben gehen (in Richtung der Object-Klasse), dann haben wir es mit einer erweiternden Umwandlung (auch bekannt als Upcasting) zu tun. Wenn wir uns in der Kette nach unten in Richtung des Objekttyps bewegen, dann ist es eine einschränkende Umwandlung (auch bekannt als Downcasting).

Das Aufsteigen in der Vererbungskette wird Erweiterung genannt, weil es zu einem allgemeineren Typ führt. Dabei verlieren wir jedoch die Möglichkeit, die der Klasse durch Vererbung hinzugefügten Methoden aufzurufen.

Code Beschreibung
public static void main(String[] args)
{
Object obj = new Tiger();
Animal animal = (Animal) obj;
Cat cat = (Cat) obj;
Tiger tiger = (Tiger) animal;
Tiger tiger2 = (Tiger) cat;
}
Bei der Einschränkung des Typs musst du einen Typumwandlungsoperator verwenden, d.h. wir führen eine explizite Umwandlung durch.

Dies veranlasst die Java-Maschine dazu, zu prüfen, ob das Objekt wirklich von dem Typ erbt, in den wir es umwandeln wollen.

Diese kleine Innovation führte zu einer gewaltigen Verringerung von Typumwandlungsfehlern und steigerte die Stabilität von Java-Programmen deutlich.

Code Beschreibung
public static void main(String[] args)
{
Object obj = new Tiger();
if (obj instanceof Cat)
{
Cat cat = (Cat) obj;
cat.doCatActions();
}}
Am besten verwendest du eine instanceof-Prüfung
public static void main(String[] args)
{
Animal animal = new Tiger();
doAllAction(animal);

Animal animal2 = new Cat();
doAllAction(animal2);

Animal animal3 = new Animal();
doAllAction(animal3);
}

public static void doAllAction(Animal animal)
{
if (animal instanceof Tiger)
{
Tiger tiger = (Tiger) animal;
tiger.doTigerActions();
}

if (animal instanceof Cat)
{
Cat cat = (Cat) animal;
cat.doCatActions();
}

animal.doAnimalActions();
}
Und hier ist der Grund. Sieh dir das Beispiel links an.

Wir (unser Code) wissen nicht immer, mit welcher Art von Objekt wir arbeiten. Es könnte ein Objekt vom gleichen Typ wie die Variable (Animal) sein, oder vom Typ irgendeiner davon abstammenden Klasse (Cat, Tiger).

Sieh dir die doAllAction-Methode an. Sie funktioniert korrekt, unabhängig von der Art des übergebenen Objekts.

Mit anderen Worten, sie funktioniert für alle drei Typen korrekt: Animal, Cat und Tiger.

public static void main(String[] args)
{
Cat cat = new Tiger();
Animal animal = cat;
Object obj = cat;
}
Hier haben wir drei Zuweisungsoperationen. Sie alle sind Beispiele für erweiternde Umwandlungen.

Der Typumwandlungsoperator wird hier nicht benötigt, da keine Überprüfung notwendig ist. Eine Objektreferenz kann immer in einer Variablen gespeichert werden, deren Typ einer ihrer Vorfahren ist.

„Oh, das vorletzte Beispiel hat alles klar gemacht: warum die Überprüfung notwendig ist und warum die Typumwandlung gebraucht wird.“

„Das hoffe ich doch. Ich möchte dich auf eine wichtige Tatsache hinweisen:“

Nichts davon bewirkt, dass sich ein Objekt in irgendeiner Weise verändert! Das einzige, was sich ändert, ist die Anzahl der Methoden, die für den Aufruf über eine bestimmte Referenzvariable zur Verfügung stehen.

Eine Cat-Variable ermöglicht beispielsweise den Aufruf der Methoden doAnimalActions und doCatActions. Sie weiß nichts von der doTigerActions-Methode, auch wenn sie auf ein Tiger-Objekt zeigt.

„Ja, ich verstehe. Das war einfacher, als ich dachte.“