Dart ist eine typsichere Sprache obwohl man keinen Typ direkt angeben muss. Wie hängt das zusammen und welche Typen bringt Dart mit? In diesem Artikel geht es um die Typisierung und Datentypen von Dart 2.5.
Statische oder dynamische Typisierung einer Variable
Dart ist entweder “stark typisiert” oder “dynamisch typisiert”. Dabei unterscheidet Dart (ab Version 2) zwischen drei Zuständen:
type annotated - Der Typ der Variable, des Parameters oder der Rückgabe ist angegeben.
inferred - Es wurde kein Typ angegeben und Dart hat den Typen erfolgreich selbst herausgefunden. Kann Dart den Typen nicht erkennt wird als Fallback der dynamic Typ verwendet.
dynamic - Wird entweder explizit im Code angegeben oder als Fallback durch inferred gesetzt.
Statische Typisierung (type annotated) hat den Vorteil eine robuste und wartbarer Code entsteht. Aber oft ist es einfach Dopplung von Code und vermindert auch oft die Lesbarkeit z.B. bei Generics oder der Verwendung von Expressions. Beispiele für statsische Typisierung bei Variablen:
var int count = 0; // int
var string message = 'hello world'; // string
var List<int> primzahlen = [2, 3, 5, 7, 11]; // Liste von int
var Set<String> haustiere = {'Hund', 'Katze', 'Maus', 'Hase'}; // Set von strings
var bool loading = false; // bool
Automatische Typisierung (ìnferred) hat den Vorteil unnötigen Code nicht schreiben zu müssen und auf den Augenmerk auf das eigentliche Statement oder den Programmfluss zu setzen. Beispiele für automatische Typisisierung:
var count = 0; // int
var message = 'hello world'; // string
var primzahlen = [2, 3, 5, 7, 11]; // Liste von int
var haustiere = {'Hund', 'Katze', 'Maus', 'Hase'}; // Set von strings
var loading = false; // bool
Gerade bei bei Zahlen oder boolschen Werten wird schon bei diesen Beispielen klar, dass man auch ohne das beschreiben des Typs ohne Probleme erkennen würde um was für einen statischen Typ es sich handelt.
Empfehlungen für die Vorgehensweise
Weil beide Varianten (type annotated und ìnferred) verschiedene Vorteile haben ist eine Mischung daraus empfehlenswert. Dart selbst trifft folgende Aussagen:
Verwende statische Typen (type annotated) wenn es um öffentliche Attribute oder Parameter bei Methoden und Top-Level Attribute in Klassen geht bei denen der Typ nicht direkt und klar ersichtlich ist. (Doku)
Ziehe in Betracht statische Typen zu verwenden wenn es sich um private Attribute und Top-Level Variablen handelt deren Typ nicht direkt zu erkennen ist. (Doku)
Verwende automatische Erkennung (ìnferred) bei Variablen innerhalb von Methoden. (Doku)
Verwende implizite Typisierung bei Function Expressions, weil der Typ in der Regel durch die Liste oder Map außerhalb bereits bekannt ist. Ist dies nicht der Fall verwende auch in der Expression den statischen Typ um Klarheit zu schaffen. (Doku)
Verwende keine doppelten Typ-Angaben bei generischen Aufrufen (Generics). Ist der Typ der Variable nicht klar darf bei dem Generics der Typ angegeben werden. Der statische Typ sollte hier nicht doppelt angegeben werden. (Doku)
Wird der Typ bei der automatischen Erkennung durch Dart falsch erkannt sollte der Typ direkt angegeben werden. Das passiert unter anderem dann wenn der Supertyp verwendet werden soll und Dart immer den direkten Typ erkennt (z.B. double anstatt number). (Doku)
Verwende dynamic wenn klar ist, dass die automatische Erkennung durch Dart fehlschlagen wird. Dadurch wird klarer das manche Teile des Codes einfach nicht typsicher sind und das dem Entwickler klar ist.
Von Dart vorgegebene Datentypen
Dart ist eine typisierte Sprache und unterstützt folgende Datentypen:
Numbers (Integer, Double)
Strings
Boolean
Lists (Arrays)
Maps
Runes
Jeder Datentyp ist ein Objekt
In Dart ist alles ein Objekt, auch wenn der Datentyp wie z.B. num oder bool klein geschrieben werden (Java Entwickler kennen das als primitiven Datentypen, welcher kein Objekt ist). Und weil jeder Datentyp ein Objekt ist, ist auch der Wert ohne Initialisierung immer null. Im Umkehrschluss ist eine null-Initialisierung auch nicht notwendig, zählt sogar als schlechter Stil.
Bei int, double und bool sollte man generell darauf verzichten null Werte zu setzen, weil man bei diesen Typen immer einen Wert erwartet. In seltenen Ausnahmen kann es aber auch von Vorteil sein, dass ein bool mal den Wert null hat. Sollte dies vorkommen sollte es auch klar dokumentiert sein unter welchen Umständen die Rückgabe hier null ist.
Integer
Ein Integer repräsentiert eine ganze Zahl ohne Kommastelle. Die Implementierung von int ist (seit Dart 2.0) ein 64bit Integer als Zweierkomplement. Der Wertebereich von int ist davon abhängig, ob es in der DartVM (-2⁶³ to 2⁶³-1) oder im Browser als Javascript (-2⁵³ to 2⁵³-1)
ausgeführt wird.
int answer = 42;
Bei sich aus dem Wert erschließenden Typen ist es guter Stil den Typ nicht mit anzugeben, was bei Zahlen fast immer der Fall ist. Ein Integer ist immer eine Zahl ohne Kommastelle:
answer = 42;
Double
Gleitkommazahlen werden in Dart mit dem Datentyp double repräsentiert. Dart doubles sind 64-bit floating-point Zahlen, wie sie im IEEE 754 Standard repräsentiert werden. Auch bei einem Double kann man in der Regeln den Typen weglassen. Hier gilt: es ist ein Double sobald es eine Nachkommastelle besitzt.
answer = 42.42; double exponents = 1.42e5;
Strings
Strings werden nahezu immer verwendet um Text darzustellen. In Dart besteht ein String aud UTF-16 Zeichen. Strings können mit einfach oder doppelten Anführungszeichen angegeben werden:
var text1 = 'Die Antwort ist 42';
var text2 = "Die Antwort ist 42";
Um einen String über mehrere Zeilen schreiben zu können, kann er in der dreifachen Variante von einfachen oder doppelten Anführungszeichen definiert werden:
var text1 = ''' Die Antwort auf die Frage nach dem Leben, dem Universum und dem ganzen Rest lautet 42. ''';
Boolean
Um Booleans zu repräsentieren hat Dart die Klasse bool. Die zwei Konstanten und reservierten Wörter trueund false sind vom Typ bool. Bei Booelans ist eine Angabe des Typs nicht notwendig.
var bool loading = true; var waiting = false;
Lists
Eine Liste ist ein Sammlung von Objekten, die über einen Index aufgerufen werden können. Der Type `List<E>’ ist per Default eine wachsende dynmische Liste.
var List<int> wichtigeZahlen = [1, 2, 3, 4];
var wichtigeZahlen2 = <int>[5, 6, 7, 8];
Maps
Eine Map enthält eine beliebige Anzahl an Keys und zu jedem Key ein Objekt. An das Objekt gelangt man dabei nur über den Key. Es können über die Keys und Values iteriert werden. Dabei definiert die Implementierung der Map, wie z.B. HashMap oder LinkedHashMap die Reihenfolge der Keys.
var hexFarben = {
'rot': '0000FF',
'grün': '#00FF00',
'blau': '#0000FF'
};
Runes
Runes sind UTF-32 Beschreibungen eines Strings. So kann auch in Dart, welches Strings mit UTF-16 Zeichen beschreibt, auch Unicode geschrieben werden. Um Runes in einem String schreiben zu können bedarf es einer speziellen Syntax:
var klatschen = '\u{1f44f}'; // 👏
Runes input = new Runes('\u{1f600}' '\u{1f499}' '\u{1f44d}');
print(new String.fromCharCodes(input)); // 😀💙👍
Wer Runes live ausprobieren möchte hier der Link zum Dartpad und die notwendige Unicode
Sehr hilfreich! Danke!