Einführung in die Umformung des Typs von Objekten einer Klasse in Java im Rahmen der Lehre des Programmierens mit der Programmiersprache Java. (cast, upcast, downcast, casting, up-cast, down-cast, Vererbung, Erweiterungen), Lektion, Seite 7215117
http://www.purl.org/stefan_ram/pub/java_klassentypwerte_umformen_de ist die kanonische URI dieser Seite.
Stefan-Ram-Startseite

Umformungen von Werten mit Klassentyp in Java

Wenn eine Klasse eine andere Klasse erweitert, dann können alle Ausdrücke der erweiternden Klasse auch als Ausdrücke der erweiterten Klasse angesehen werden.

Beispielsweise erweitert die Klasse "java.io.PrintStream" die Klasse "Object". Daher kann jeder Ausdruck vom Typ "java.io.PrintStream" auch als ein Ausdruck vom Typ "Object" interpretiert werden.

In dem Programm "Filter.java" wird die Typwandlung mit dem Formungsoperator "( Object )" durchgeführt. Der Ausdruck "(( Object )System.out )" hat tatsächlich den Typ "Object". Da für Ausdrücke dieser Klasse aber keine Methode "println" haben, scheitert dann der Aufruf dieser Methode.

ObjectPrinter.java
public class ObjectPrinter 
{ final public static void main( String[] args ) 
{ (( Object )System.out ).println( 447 ); }}

Konsole
javac Filter.java
Filter.java:3: cannot resolve symbol 
symbol : method println (int) 
location: class java.lang.Object 
{ (( Object )System.out ).println( 447 ); }} 
^

Für Ausdrücke der Klasse "Object" ist aber eine Methode "hashCode" festgelegt, die dann auch aufgerufen werden kann. (Diese Methode hat allerdings keine Wirkung.)

ObjectCode.java
public class ObjectCode 
{ final public static void main( String[] args ) 
{ (( Object )System.out ).hashCode(); }}

System.out

Die Methode "hashCode" haben auch Ausdrücke der erweiternden Klasse "java.io.PrintStream", so daß sie für einen Ausdruck dieser Klasse auch ohne die obige Typformung aufgerufen werden kann.

OutCode.java
public class OutCode 
{ final public static void main( String[] args ) 
{ System.out.hashCode(); }}

System.out

Daß ein Ausdruck der erweiternden Klasse "java.io.PrintStream" auch die Methode "hashCode" der erweiterten Basisklasse "java.io.FilterOutputStream" hat, liegt einfach daran, daß die Klasse "java.io.PrintStream" die Klasse "Object" erweitert. Ein Ausdruck einer erweiternden Klasse hat nämlich immer alle Einträge der erweiterten Basisklasse, zuzüglich eventueller Erweiterungen der erweiternden Klasse, wie es sich schon aus der Bedeutung des Verbs „erweitern“ ergibt.

Das heißt, ein Ausdruck der Klasse "java.io.PrintStream" hat alle Methoden eines Ausdrucks der Basisklasse "Object" und eventuell noch weitere zusätzliche Methoden. Umgekehrt muß aber ein Ausdruck der Basisklasse "Object" nicht alle Methoden einer erweiternden Klasse haben. Beispielsweise hat ein Ausdruck der Basisklasse "Object" nicht  die Methode "println", weil diese erst von der erweiternden Klasse "java.io.PrintStream" festgelegt wurde. Da die Methode "hashCode" aber schon in der Basisklasse "Object" festgelegt ist, gehört sie auch zur erweiternden Klasse "java.io.PrintStream".

Das Klassendiagramm "PrintStream" gibt die Situation wieder (es wurde vereinfacht).

PrintStream [UML class diagram]
.---------------------------------. 
| Object | 
|---------------------------------| 
|---------------------------------| 
| + hashCode() : int | 
'---------------------------------' 

/_\ 


.---------------------------------. 
| java.io.PrintStream
|---------------------------------| 
|---------------------------------| 
| + println() : void | 
'---------------------------------'

Ausdrucktyp und Werttyp

Der Ausdruck "(( Object )System.out )" hat in einem gewissen Sinne zwei verschieden Typen: Einen Ausdrucktyp  und einen Werttyp.

Der Typ des Ausdrucks  ist die Klasse "Object", der Typ des Wertes  des Ausdrucks ist jedoch weiterhin "java.io.PrintStream".

Die verfügbaren Einträge auf der rechten Seite des Zeichens "." werden durch den statischen Typ des Ausdrucks auf der linken Seite des Zeichens "." bestimmt und nicht durch den Typ des Wertes. Daher ist "(( java.lang.Object )System.out ).println" keine gültige Angabe einer Methode.

Der Typ des Ausdrucks "(( Object )System.out )" wird hier als „statisch“ bezeichnet, weil er bereits durch den (zur Laufzeit nicht veränderlichen, also „statischen“) Quelltext bestimmt ist und nicht von Umständen während der Ausführung abhängt.

Aus dem Ausdrucks "(( Object )System.out )" kann jedoch wieder ein Ausdruck mit dem ursprünglichen Typ des Wertes "System.out" gebildet werden.

Abwärtsformung

Die bisher verwendete Typformung war eine Aufwärtsformung. Eine Basisklasse einer Klasse wird auch als Oberklasse  einer Klasse angesehen. Eine Klasse wird auch als Unterklasse  ihrer Basisklassen angesehen. Auch in dem Diagramm "PrintStream" befindet sich ja die Basisklasse über  ihrer Unterklasse.

Etwas allgemeiner, aber in dieser Lektion praktisch synonym, sind die Begriffe Obertyp  und Untertyp, die auch anstelle von Oberklasse und Unterklasse gebraucht werden können.

Da die verwendete Typformung den Typ eines Ausdrucks in einen allgemeineren Obertyp (also den Typ einer Oberklasse des Ausgangstyps) wandelte, nennt man sie auch eine Aufwärtsformung  (upcast ).

Daneben gibt es aber auch eine Abwärtsformung  (downcast ), die zu einem Ausdruck mit dem Typ einer Klasse einen Ausdruck mit dem Typ einer Unterklasse ergibt. Auch hier ist der entscheidende Vorgang die Prüfung, ob der Wert des Ausdrucks solch eine Wandlung erlaubt.

Formungen
             .---------------------------------. 
| Object | 
|---------------------------------| 
^ |---------------------------------| | 
| | + hashCode() : int | | 
| '---------------------------------' | 
| ^ | 
| ( Object ) /_\ ( java.io.PrintStream ) |  
| | |  
| | | 
| .---------------------------------. | 
| | java.io.PrintStream | V 
|---------------------------------| Abwaertsformung 
|---------------------------------| 
| + println() : void | 
'---------------------------------'

Eine Aufwärtsformung ist dann möglich, wenn der Typ des Umformungsoperators eine Basisklasse des Typs des Operanden-Ausdrucks ist.

Eine Abwärtsformung ist dann möglich, wenn der Typ des Umformungsoperators der Typ des Werts des Operanden-Ausdrucks oder ein Obertyp des Werts der Operanden-Ausdrucks ist.

ObjectPrinter2.java
public class ObjectPrinter2 
{ final public static void main( String[] args ) 
{ ( ( java.io.PrintStream ) /* Abwaertsformung zurueck */ 
( ( Object ) System.out )) /* Aufwaertsformung nach Object */ 
.println( 447 ); }}

System.out
447

Ein Ausdruck einer Klasse hat alle Methoden dieser Klasse und auch alle Methoden aller Basisklassen seiner Klasse. Durch den Formungsoperator kann ein Ausdruck einer Klasse in einen Übertyp oder Untertyp gewandelt werden.

▶   Streuwert ausgeben
In den vorangegangenen Beispielprogrammen wurde ein Streuwert ermittelt, aber nicht ausgegeben. Schreiben Sie einer Programm, das den berechneten Streuwert auch ausgibt.

Von der Stefan-Ram-Startseite ausgehend finden sich oft noch mehr Informationen zu Themen, die auf einer Seite angesprochen wurden. (Eine Verbindung zur Stefan-Ram-Startseite befindet sich ganz oben auf dieser Seite.)  |   Seiteninformation und Impressum  |   Formular für diese Seite betreffende Mitteilungen an den Herausgeber  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. slrprd, PbclevtugFgrsnaEnz