8 Typwandlungen

Transcription

8 Typwandlungen
8
Typwandlungen
Hier geben wir Ihnen weitere Informationen zu Goto Java 2, die Sie ergänzend
zu Hausaufgabe 3 lesen sollten.
Sie sollten für die weiteren Ausführungen zunächst in Go To Java Kapitel 4.6
über “Typkonvertierunen” lesen. Dort wird besprochen, welche Typkonvertierungen der Java–Compiler automatisch vornehmen kann — leider werden keine
Beispiele für Typkonvertierungen gebracht, und es wird auch nicht erwähnt,
warum Typkonvertierungen wichtig sind.
8.1
Automatische Typkonvertierungen
Im vorigen Kapitel haben Sie gelernt, daß jeder Ausdruck in einem Java–Programm
einen Datentyp hat. Manchmal ist es notwendig, einen Ausdruck eines gewissen Datentyps in einen anderen Datentyp umzuwandeln. Teilweise werden diese
Umwandlungen durch den Java–Compiler automatisch vorgenommen, teilweise
muß man sie explizit befehlen.
Warum Typwandlungen überhaupt nötig sind, soll an einem Beispiel demonstriert werden. Es seien die folgenden Variablen definiert:
int i1 = 5;
int i2 = 3;
float f1 = 3.123f;
float f2 = 2.723f;
double d1 = 3.1234567890123456;
Jede Rechnung mit diesen verschiedenen Variablen muß irgendwie vom Prozessor ausgeführt werden. Besprechen wir das an den Beispielen der Addition und
der Multiplikation.
Übliche Prozessoren enthalten Funktionen, mit denen zwei Integer addiert oder
multipliziert werden können. Sie haben auch Funktionen, mit denen zwei Float
oder zwei sonstige Datentypen addiert oder multipliziert werden können. Das
Ergebnis dieser Funktionen hat dann normalerweise den gleichen Datentyp wie
die Operanden der Rechnung.
Beispiel: Wenn Sie mit zwei int–Variablen rechnen, kann der Prozessor
die Rechnung direkt vornehmen. Das Ergebnis ist dann immer wieder ein
Integer (überlegen Sie sich, warum das Ergebnis der Division nicht der
Erwartung entspricht !)
i1 + i2; // Ergebnis: int, 8
i1 / i2; // Ergebnis: int, 1
i1 * i2; // Ergebnis: int, 25
Genauso ist das Ergebnis beim Rechnen mit float–Variablen immer wieder ein Float:
f1 + f2; // Ergebnis: float, 5.846
f1 / f2; // Ergebnis: float, 1.1468968...
f1 * f2; // Erbebnis: float, 8.503929
60
Normalerweise enthalten Prozessoren keine Funktionen, die gleichzeitig mit verschiedenen Datentypen rechnen können. Will man also mit zwei Werten verschiedener Datentypen miteinander verrechnen, müssen erst beide Datentypen
in einen gemeinsamen Datentyp konvertiert werden. Danach kann die Rechnung
in diesem Datentyp stattfinden.
Beispiel: Prozessoren sind meist nicht dazu in der Lage, einen IntegerWert und einen Float oder einen Float und einen Double zu addieren.
Wenn Sie die Rechnung
i1 + f1;
durchführen wollen, muß sie also vorher noch aufbereitet werden. Der
Prozessor kann entweder zwei Integer oder zwei Float addieren. Es bieten
sich daher zwei Möglichkeiten an, um die Rechnung mit den Funktionen
des Prozessors durchzuführen:
• Wandlung der Float-Variable zu einem Integer und Durchführung
der Addition als Integer-Addition:
i1 + (int)f1;
Dabei bedeutet der Ausdruck (int)f1: Wandele f1 in einen Integer–
Wert um. In der Regel geschieht eine solche Umwandlung durch
Wegwerfen aller Nachkommastellen.
Mit den oben angegebenen Werten würde diese Rechnung wie folgt
ausgeführt:
i1 + (int)f1 == 5 + (int)3.123 == 5 + 3 == 8
• Wandlung des Integers zu einem Float und Durchführung der Addition als Float–Addition:
(float)i1 + f1;
Hier bedeutet der Ausdruck (float)i1: Wandle den Integer i1 in
einen Float–Wert um. Dies geschieht ohne Verlust von Information.
Mit den oben angegebenen Werten würde diese Rechnung so ausgeführt:
(float)i1 + f1 == (float)5 + 3.123 == 5.0 + 3.123 == 8.123
Die Antwort, welche der beiden Möglichkeiten gewählt wird, fällt leicht:
Wenn wir den Float–Wert zu einem Integer wandeln, verschenken wir
Rechengenauigkeit und kommen zu falschen Ergebnissen. Wenn wir den
Integer hingegen in einen Float wandeln, erhalten wir das erwartete Ergebnis.
Bei Typwandlungen, die der Compiler automatisch vornimmt, wird immer darauf geachtet, keine Genauigkeit zu zerstören. Datentypen werden in Rechnungen
darum nur zu genaueren Datentypen umgewandelt.
Abbildung 4.1 im Kapitel 4.6 von “Go To Java 2” zeigt genau dies — ein short,
der ja nur die ganzen Zahlen von −21 5 bis 21 5 − 1 darstellen kann, kann automatisch in die umfangreicheren Datentypen int, long oder float umgewandelt
werden. Hingegen wird der Datentyp double niemals automatisch in einen anderen Datentyp gewandelt, da dies der genaueste und umfangreichste Datentyp
der Sprache Java ist.
61
Typkonvertierungen treten immer auf, wenn ein Ausdruck eines gewissen Typs
erwartet wird, aber ein anderer Typ vorliegt. Es kommt des öfteren vor, daß in einem Ausdruck ein niedriger Genauigkeit benötigt wird, der zu benutzende Wert
aber eine hohe Genauigkeit hat. In solch einem Fall würde eine Typwandlung
Genauigkeit verschenken. Der Java–Compiler würde daher statt eine automatische Typwandlung vorzunehmen, das Programm als fehlerhaft bemäkeln.
Ein gutes Beispiel für Fälle, in denen eine Typkonvertierung zu ungenaueren Typen nötig ist, bieten die Zuweisungsoperatoren. Der einer Variable zugewiesene
Wert muß ja immer den Datentyp der Variable haben. Wird nun einer Variable
eines “ungenauen” Datentyps ein Wert eines genaueren Datentyps zugewiesen,
so erzeugt das einen Kompilationsfehler.
Beispiel: Zum Beispiel sind folgende Anweisungen erlaubt, da eine
Wandlung zu einem genaueren Typ stattfindet:
f1 = i1;
d1 = i1;
d1 = f1;
// bei Zuweisung: autom. Wandlung i1 zu float
// bei Zuweisung: autom. Wandlung i1 zu double
// bei Zuweisung: autom. Wandlung f1 zu double
Alle oben genannten Zuweisungen sind erlaubt, da hier bei den automatischen Typwandlungen keine Genauigkeit verlorengeht.
Hingegen sind die folgenden Anweisungen nicht erlaubt:
i1 = f1;
i1 = d1;
f1 = d1;
// verboten, da float genauer als int
// verboten, da double genauer als int
// verboten, da double genauer als float
In jedem dieser Fälle würde Genauigkeit verlorengehen, und darum würde
der Compiler mit einer Fehlermeldung gegen jede der Anweisungen protestieren. Die Fehlermeldung könnte wie folgt aussehen:
Programmname.java:11: Incompatible type for =. Explicit cast
needed to convert float to int.
i1 = f1;
Beispiel: Sehr häufig kommen derartige Fehlermeldungen in Programmen vor, in denen man Float–Variablen einen Wert zuweist. Sie haben ja
im vorigen Kapitel gelernt, daß reellwertige Zahlenliterale den Datentyp
double erhalten, wenn sie nicht das Anhängsel “f” haben. Die Zuweisung
eines double–Literals an eine float–Variable bedingt einen Genauigkeitsverlust und führt daher zu einem Compiler–Fehler:
01
02
03
04
05
06
07
08
09
10
11
12
int
i;
i = 1;
i = 1L;
i = 3.141;
float f;
f = 1;
f = 3.141f;
f = 3.141;
double d;
d = 1l;
d = 3.141f;
d = 3.141;
// erlaubt: Zuweisung int-Literal an int
// Fehler: long-Literal, aber int erwartet
// Fehler: Zuweisung double-Literal an int
// erlaubt: Zuweisung int-Literal an float
// erlaubt: Zuweisung float-Literal an float
// Fehler: double-Literal, doch float nötig
// erlaubt: Zuweisung long-Literal an float;
// erlaubt: Zuweisung float-Literal an double
// erlaubt: Zuweisung double-Literal an double
62
8.2
Manuelle Typkonvertierungen
Oft will man trotzdem Werte höherer Genauigkeit an Stellen verwenden, an
denen eine niedrigere Genauigkeit benötigt wird. Wieder ist die Zuweisung eines
Wertes an eine Variable das beste Beispiel für derartige Fälle.
Wenn wir beispielsweise wissen, daß in einer float–Variable ein Wert ohne Nachkommastellen enthalten ist, sollten wir in der Lage sein, diesen als Integer aufzufassen.
Oftmals brauchen wir die höhere Genauigkeit auch nicht wirklich. Es ist zum
Beispiel üblich, die Zwischenschritte einer Rechnung in einer hohen Genauigkeit
durchzuführen und dann das Endergebnis der Rechnung nur in einer niedrigen
Genauigkeit zu verwenden.
Java nimmt ohne unser Zutun solche Typwandlungen nicht vor. Wir können
Java aber befehlen, eine Typwandlung vorzunehmen. Wir verwenden dazu einen
Typwandlungsoperator und signalisieren Java damit: An dieser Stelle ist es in
Ordnung, Genauigkeit zu verschenken, wir wissen schon was wir tun.
Definition: Type–cast
Eine manuelle Typwandlung nennt man type–cast.
Um einen Wert explizit in anderen Datentyp zu wandeln, schreibt man den
Datentyp, zu dem der Wert gewandelt werden soll, in runde Klammern und den
Wert dahinter:
(datentyp) wert
Dies ist die Anweisung an den Compiler eine Typwandlung vorzunehmen.
Beispiel: Folgende Zuweisungen sind erlaubt, obwohl bei der Wandlung
Genauigkeit verloren geht:
i1 = (int)f1;
i2 = (int)d1;
f1 = (float)d1;
Mit den oben definierten Werten für die einzelnen Variablen ergeben sich
hier die folgenden Werte für i1 bis f1:
i1 == (int)f1 == (int)3.123 == 3
i2 == (int)d1 == (int)3.1982 == 3
f1 == (float)d1 == (float)3.1234567890123456 == 3.1234567
Ein Wert niedrigerer Genauigkeit ergibt sich dabei meist aus einem Wert
höherer Genauigkeit durch wegwerfen von Nachkommastellen.
Sie können bei jedem Ausdruck eine Typwandlung vornehmen. Sie dürfen Typwandlungen auch an Stellen vornehmen, an denen sie überflüssig sind.
Beispiel: Die folgenden Type–Casts sind zwar erlaubt aber überflüssig,
da Java hier einen automatischen Type–Cast vornehmen würde¿
63
Type–cast
f1 = (float)i1;
d1 = (double)f1;
Allerdings sind Typwandlungen nur dort gestattet, wo sie Sinn machen.
Beispiel: Beispielsweise kann man Zahlen nur untereinander umwandeln,
wobei der Datentyp char auch als Zahl zählt. Folgender Programmtext
führt zu einem Fehler:
int i1 = (int)"Ein Text"; // Fehler
Eine wichtige Besonderheit ist bei Typwandlungen noch zu erwähnen: Die einzelnen primitiven Datentypen sind nicht nur unterschiedlich genau. Auch der
Wertebereich unterscheidet sich. Auf diese Problematik wollen wir hier aber
nicht weiter eingehen — hier nur ein Beispiel dazu:
Beispiel: Wenn Sie einen zu großen Double–Wert in einen Float wandeln,
wird der Wert als Infinity dargestellt:
double d = 1.23e300; // ist nicht als float darstellbar
float f = (float) d; // jetzt enthält f den Wert Infinity
8.3
Zusammenfassung
Sie müssten nun die folgenden Fragen beantworten können:
⊲ Warum sind Typkonvertierungen nötig ?
⊲ Welche Arten von Typkonvertierungen führt Java automatisch durch ?
⊲ Geben Sie einige Beispiele, in denen Typkonvertierungen automatisch
durchgeführt werden !
⊲ Warum führt Java manche Typkonvertierungen nicht automatisch
durch ?
⊲ Geben Sie einige Beispiele, in denen statt einer automatischen Typkonvertierung ein Fehler ausgegeben wird !
⊲ Wie kann man Java dazu zwingen, einen Wert in einen anderen Typ
zu wandeln ?
⊲ Nennen Sie einige Beispiele für überflüssige Typwandlungen !
64