Kapitel 2: Grundlagen / 2.6 Konvertierung von Zahlentypen

Die vielen Integer Datentypen und die beiden Gleitkommatypen Double und Float, das sind alles unterschiedliche Datentypen. Bereits im Beispiel haben Sie gesehen, dass der Versuch ein Double zu einem Float zu addieren oder einem Int den Wert eines Int8 zuzuweisen bereits zur Compilezeit zu einem Fehler führt.

Sie können diese Typgrenzen allerdings überschreiten, dazu verwenden Sie die Form:

NeuerTyp(variableAltenTyps)

Dies funktioniert für praktisch alle offensichtlich ineinander überführbaren Typen, nicht nur für die Zahlentypen, aber für diese insbesondere: Die Integertypen können ineinander umgewandelt werden, genauso die Gleitkommatypen, es kann ein Gleitkommatyp in einen Integertyp umgewandelt werden (wobei alle Nachkommastellen abgeschnitten werden) oder umgekehrt auch ein Integertyp in einen Gleitkommatyp. Ein Beispiel:

var int1: UInt16 = 100
var int2: UInt8 = UInt8(int1)
println(int2)

Erzeugt die Ausgabe:

100

Da das so gut funktioniert, fragen Sie sich vielleicht, warum Sie den neuen Typ überhaupt explizit angeben müssen. Es gibt doch Inference und dass Sie den Datentyp wechseln wollen, sollte dem Compiler spätestens dann klar werden, wenn Sie einem UInt8 einen UInt16 zuweisen. Das folgende Beispiel illustriert das Problem:

var int1: UInt16 = 1000
var int2: UInt8 = UInt8(int1)
println(int2)

Dieser Code führt im Playground zu einer Fehlermeldung, und zwar zu einer fiesen: Statt eines knappen Fehlertextes bekommen sie einen sogenannten Stacktrace zu sehen, das ist eine Aufrufhistorie des compilierten Programms, bestehend aus sehr viel Text und irgendwo darin die Fehlerursache: EXC_BAD_INSTRUCTION, das kann alles bedeuten. Damit ist Ihnen Ihre erste Runtime Exception um die Ohren geflogen, Ihr erster Laufzeitfehler.

Zu dem Fehler ist es gekommen, weil im Code die Grenzen von UInt8 überschritten werden. Und da der Typ explizit konvertiert wurde, haben wir den Compiler mitgeteilt: „Ich weiß dass es sich um unterschiedliche Typen handelt, aber keine Sorge, ich übernehme die Verantwortung.“ Und der Compiler hat geantwortet: „Alles klar, dann lass ich dir das ausnahmsweise mal durchgehen.“ Naja, und jetzt haben wir den Laufzeitfehler.

Durch eine Typkonvertierung machen Sie also garantierte Compilezeitfehler zu möglichen Laufzeitfehlern. Verwenden Sie darum die Typkonvertierung bitte nur dann, wenn Sie die zu konvertierenden Werte genau genug abschätzen können, so dass es nicht zu einem Fehler führt.

Ich hatte das Thema schon einmal angesprochen, aber weil es sehr wichtig ist noch einmal zur Vertiefung: Bei der Verwendung des Playgrounds ist es natürlich egal, ob ein Fehler zur Compilezeit oder zur Laufzeit geschieht. Im Playground wird ad hoc compiliert, sobald sich der eingegebene Text geändert hat, so dass praktisch jeder Fehler zur Laufzeit geschieht. Wenn Sie aber ein Programm schreiben, dann compilieren Sie es zunächst und lassen es dann ablaufen. Einen Fehler zur Compilezeit sehen sie also sofort, er kann Ihnen nicht entgehen, während ein Fehler zur Laufzeit nur dann für Sie sichtbar wird, wenn der fehlerhafte Code erstens auch tatsächlich durchlaufen wird und zweitens dies unter Umständen geschieht, die den Fehler auch auslösen, zum Beispiel könnte ein Fehler nur dann auftreten, wenn der Benutzer in einem Eingabefeld eine 0 eingibt. Bei Fehlern zur Laufzeit sind Sie immer auf Tests angewiesen und außer bei sehr kleinen Programmen bleibt immer ein Restrisiko, einige Laufzeitfehler nicht gefunden zu haben. Bugs sind immer Laufzeitfehler.

Ich möchte noch kurz darauf eingehen, was technisch bei einer Typkonvertierung geschieht. Sie müssen diesen Teil noch nicht verstehen, ein späteres Kapitel wird sich detailliert mit Klassen beschäftigen.

Technisch betrachtet ruft IrgeneinTyp(initialerWert) einen Konstruktor – Apple nennt ihn Initializer – der Klasse IrgendeinTyp auf. Durch den Aufruf des Konstruktors wird ein Objekt vom Typ IrgeneinTyp erzeugt (genauer: es wird eine Instanz dieses Objektes erzeugt). Im Beispielcode erzeugt UInt8(int1) ein Objekt vom Typ UInt8.

Durch die Übergabe eines Parameters an den Konstruktor wird das neue Objekt gleich initialisiert, also mit einem Wert vorbelegt. Im Beispiel mit dem Wert von int1. Genaugenommen konvertiert die in diesem Kapitel beschriebene Methode also nicht den alten Typ, sondern erzeugt schlicht ein neues Objekt vom gewünschten Typ auf Basis des alten.

Da Konstruktoren zudem durch sog. Extensions auch noch nachträglich an bestehende Klassen angefügt werden können, ist es möglich auch bisher nicht bestehende Typkonvertierungen zu ergänzen. Wie das geht, findet sich in einem späteren Kapitel.

Kapitel 2.6: Konvertierung von Zahlentypen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.