Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 4 - Datentypen

## 4.6 Typkonvertierungen

- 4.6 Typkonvertierungen
  - 4.6.1 Standardkonvertierungen
  - 4.6.2 Vorzeichenlose Bytes

### 4.6.1 Standardkonvertierungen

Es gibt diverse Konvertierungen zwischen unterschiedlichen Datentypen in Java. Diese werden einerseits vom Compiler automatisch vorgenommen, beispielsweise bei der Auswertung von numerischen Ausdrücken. Andererseits können sie verwendet werden, um mit Hilfe des Type-Cast-Operators (siehe Kapitel 5) eigene Konvertierungen vorzunehmen.

Java unterscheidet prinzipiell zwischen erweiternden und einschränkenden Konvertierungen und diese noch einmal nach primitiven Typen und Referenztypen. Zunächst zu den Referenztypen:

- Als erweiternde Konvertierung eines Referenztyps `T` wird vor allem die Umwandlung eines Objekts vom Typ `T` in eine seiner Vaterklassen angesehen.
- Als einschränkende Konvertierung eines Referenztyps `T` wird vor allem die Umwandlung eines Objekts vom Typ `T` in eine der aus `T` abgeleiteten Klassen angesehen.

Daneben gibt es noch eine ganze Reihe weiterer Regeln zur Definition von erweiternden und einschränkenden Konvertierungen von Referenztypen. Die Bedeutung von Vaterklassen und den daraus abgeleiteten Unterklassen wird in Kapitel 8 ausführlich erläutert.

Konvertierungen auf primitiven Datentypen sind etwas aufwendiger zu erklären. Wir benutzen dazu Abbildung 4.1:

Abbildung 4.1: Konvertierungen auf primitiven Datentypen

Jede Konvertierung, die in Pfeilrichtung erfolgt, beschreibt eine erweiternde Konvertierung, und jede Konvertierung, die entgegen der Pfeilrichtung erfolgt, beschreibt eine einschränkende Konvertierung. Andere Konvertierungen zwischen primitiven Datentypen sind nicht erlaubt. Insbesondere gibt es also keine legale Konvertierung von und nach `boolean` und auch keine Konvertierung zwischen primitiven Typen und Referenztypen.

Welche Bedeutung haben nun aber die verschiedenen Konvertierungen zwischen unterschiedlichen Typen? Wir wollen uns an dieser Stelle lediglich mit den Konvertierungen zwischen primitiven Typen beschäftigen. Wie aus Abbildung 4.1 ersichtlich ist, beschränken sich diese auf Umwandlungen zwischen numerischen Typen. Die Anwendung einer erweiternden Konvertierung wird in folgenden Fällen vom Compiler automatisch vorgenommen:

- Bei einer Zuweisung, wenn der Typ der Variablen und des zugewiesenen Ausdrucks nicht identisch ist.
- Bei der Auswertung eines arithmetischen Ausdrucks, wenn Operanden unterschiedlich typisiert sind.
- Beim Aufruf einer Methode, falls die Typen der aktuellen Parameter nicht mit denen der formalen Parameter übereinstimmen.

Es ist daher beispielsweise ohne weiteres möglich, ein `short` und ein `int` gemeinsam in einem Additionsausdruck zu verwenden, da ein `short` mit Hilfe einer erweiternden Konvertierung in ein `int` verwandelt werden kann. Ebenso ist es möglich, ein `char` als Array-Index zu verwenden, da es erweiternd in ein `int` konvertiert werden kann. Auch die Arithmetik in Ausdrücken, die sowohl integrale als auch Fließkommawerte enthalten, ist möglich, da der Compiler alle integralen Parameter erweiternd in Fließkommawerte umwandeln kann.

Es ist dagegen nicht ohne weiteres möglich, einer `int`-Variablen einen `double`-Wert zuzuweisen. Die hierzu erforderliche einschränkende Konvertierung nimmt der Compiler nicht selbst vor; sie kann allerdings mit Hilfe des Type-Cast-Operators manuell durchgeführt werden. Auch die Verwendung eines `long` als Array-Index verbietet sich aus diesem Grund.

|  |  |  | Bei den einschränkenden Konvertierungen kann es passieren, daß ein Wert verfälscht wird, da der Wertebereich des Zielobjekts kleiner ist. Aber auch erweiternde Konvertierungen sind nicht immer gefahrlos möglich. So kann zwar beispielsweise ein `float` mindestens genauso große Werte aufnehmen wie ein `long`. Seine Genauigkeit ist aber auf ca. 8 Stellen beschränkt, und daher können größere Ganzzahlen (z.B. 1000000123) nicht mehr mit voller Genauigkeit dargestellt werden. |  |
Warnung

### 4.6.2 Vorzeichenlose Bytes

In Java sind alle numerischen Datentypen vorzeichenbehaftet. Das ist in vielen Fällen sinnvoll, kann aber bei der Handhabung von 8-Bit-Bytes hinderlich sein. Wird ein Byte als Repräsentation eines 8-Bit langen Maschinenworts angesehen, will man meist den Wertebereich von 0 bis 255 zur Verfügung haben. Als vorzeichenbehafteter Datentyp kann `byte` aber nur Werte von -128 bis 127 darstellen. Ein Wert größer oder gleich 128 erfordert also mindestens ein `short` oder ein `int`. Deren Länge beträgt aber 2 bzw. 4 Byte.

Das Dilemma läßt sich dadurch auflösen, daß man zwischen der programminternen Verarbeitung eines Bytes und seiner äußeren Repräsentation unterscheidet. Die Repräsentation nach außen erfolgt dabei mit dem Datentyp `byte`. Zur Verarbeitung im Programm wird er dagegen in ein `int` konvertiert, so daß alle Werte von 0 bis 255 dargestellt werden können. Konvertierungsmethoden erlauben es, zwischen beiden Darstellungen zu wechseln.

Natürlich gibt es keinerlei automatischen Schutz gegen Wertebereichsüberschreitungen, wenn ein Byte als `int` verarbeitet wird. Dafür ist ausschließlich die Anwendung selbst verantwortlich.

Das folgende Listing zeigt eine einfache Klasse `ByteKit`, mit der zwischen beiden Darstellungen gewechselt werden kann:

    001 /**
    002  * ByteKit
    003  *
    004  * Einfache Klasse zur Umwandlung zwischen int, char und
    005  * vorzeichenlosen Bytes.
    006  */
    007 public class ByteKit
    008 {
    009   /**
    010    * Wandelt value (0 <= value <= 255) in ein byte um.
    011    */
    012   public static byte fromUnsignedInt(int value)
    013   {
    014     return (byte)value;
    015   }
    016
    017   /**
    018    * Wandelt c in ein byte um. Das High-Byte wird ignoriert.
    019    */
    020   public static byte fromChar(char c)
    021   {
    022     return (byte)(c & 0xFF);
    023   }
    024
    025   /**
    026    * Betrachtet value als vorzeichenloses byte und wandelt
    027    * es in eine Ganzzahl im Bereich 0..255 um.
    028    */
    029   public static int toUnsignedInt(byte value)
    030   {
    031     return (value & 0x7F) + (value < 0 ? 128 : 0);
    032   }
    033
    034   /**
    035    * Betrachtet value als vorzeichenloses byte und wandelt
    036    * es in ein Unicode-Zeichen mit High-Byte 0 um.
    037    */
    038   public static char toChar(byte value)
    039   {
    040     return (char)toUnsignedInt(value);
    041   }
    042
    043   /**
    044    * Liefert die Binaerdarstellung von value.
    045    */
    046   public static String toBitString(byte value)
    047   {
    048     char[] chars = new char[8];
    049     int mask = 1;
    050     for (int i = 0; i < 8; ++i) {
    051       chars[7 - i] = (value & mask) != 0 ? '1' : '0';
    052       mask <<= 1;
    053     }
    054     return new String(chars);
    055   }
    056 }
| ByteKit.java

Listing 4.12: Umwandlung zwischen int, byte und char

Eine einfache Anwendung der Klasse `ByteKit` zeigt das folgende Programm:

    001 /* Listing0413 */
    002
    003 public class Listing0413
    004 {
    005   public static void main(String[] args)
    006   {
    007     for (int i = 0; i < 256; ++i) {
    008       System.out.print("i=" + i);
    009       byte b = ByteKit.fromUnsignedInt(i);
    010       System.out.print(" b=" + ByteKit.toBitString(b));
    011       char c = ByteKit.toChar(b);
    012       System.out.print(" c=" + (c >= 32 ? c : '.'));
    013       System.out.println();
    014     }
    015   }
    016 }
| Listing0413.java

Listing 4.13: Anwendung der Klasse ByteKit

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de