Variablen in Dart

Variablen

Lernen Sie die Variablen in Dart kennen. Unten sehen Sie ein Beispiel für das Erstellen und Initialisieren einer Variablen:

var name = 'Jack';Code-Sprache: JavaScript (javascript)

Eine Variable speichert eine Referenz. Die Variable namens name enthält eine Referenz, die auf das String-Objekt mit dem Wert „Bob“ verweist.

Eine Referenz ist eine Speicheradresse im Arbeitsspeicher. Damit Daten einfach im Speicher abgelegt werden können, bekommt jeder Speicherbereich eine eindeutige Adresse zugewiesen – vergleichbar mit einer Hausnummer an einem Haus.

Der Compiler leitet den Typ von name automatisch als String ab. Sie können den Typ aber auch manuell angeben, um die automatische Typableitung zu überschreiben. Wenn eine Variable Objekte unterschiedlicher Typen aufnehmen soll, deklarieren Sie ihren Typ als Object (bei Bedarf können Sie auch dynamic verwenden).

Object name = 'Jack';Code-Sprache: JavaScript (javascript)

Eine weitere Schreibweise ist die explizite Typangabe (wenn wir beispielsweise wissen, dass ein Name Text ist und als String gespeichert wird, geben wir den Typ einfach direkt an):

String name = 'Jack';Code-Sprache: JavaScript (javascript)

Null-Sicherheit

Die Null-Sicherheit verhindert Fehler durch versehentlichen Zugriff auf Variablen mit dem Wert null. Solche Fehler werden als Null-Dereferenzierungsfehler bezeichnet. Ein Null-Dereferenzierungsfehler tritt auf, wenn Sie auf Eigenschaften zugreifen oder Methoden eines Ausdrucks aufrufen, dessen Ergebnis null ist.

Es gibt eine Ausnahme: Wenn null selbst die entsprechende Eigenschaft oder Methode unterstützt (beispielsweise toString(), hashCode), wird kein Fehler ausgegeben. Mithilfe der Null-Sicherheit erkennt der Dart-Compiler solche potenziellen Fehler bereits zur Kompilierzeit.

Entwickler mit Programmiererfahrung wissen, dass Null-Dereferenzierungsfehler sehr häufig auftreten. Wenn diese Fehler bereits zur Kompilierzeit abgefangen werden können, verbessert sich die Stabilität und Robustheit des Programms erheblich.

Beispiel: Angenommen, Sie möchten den Absolutwert der Integer-Variablen i abfragen. Wenn i den Wert null hat, löst der Aufruf i.abs() einen Null-Dereferenzierungsfehler aus. In anderen Programmiersprachen würde dieser Code erst zur Laufzeit einen Fehler werfen; der Dart-Compiler verbietet solche ungültigen Aufrufe direkt und fängt das Problem im Vorfeld ab.

Drei zentrale Änderungen durch die Null-Sicherheit:

1. Nullbare Typen: Bei der Typangabe können Sie steuern, ob der Typ den Wert null zulässt. Fügen Sie dazu einfach ein Fragezeichen am Ende des Typs hinzu.

String? name  // Nullbarer Typ: Wert kann null oder ein String sein
String name   // Nicht nullbarer Typ: Darf nicht null sein, nur Strings speichernCode-Sprache: JavaScript (javascript)

In manchen Programmiersprachen kann eine Variable wie string name auch null speichern – in Dart ist das nicht erlaubt. Wenn null gespeichert werden soll, muss der Typ String? verwendet werden.

2. Variablen müssen vor der Verwendung initialisiert werden. Nullbare Variablen haben standardmäßig den Wert null, also eine implizite Initialisierung. Nicht nullbare Typen besitzen keinen Standardwert und zwingen Sie zur manuellen Zuweisung eines Anfangswerts. Dart verbietet den Zugriff auf nicht initialisierte Variablen, um einen Fehlerfall auszuschließen.

void main() {
  int year;        // Fehler: Nicht nullbare Variable ohne Initialisierung
  print(year);     // Kompilierfehler: year wurde nicht initialisiert
}
Code-Sprache: Dart (dart)

Der obige Code erzeugt einen Kompilierfehler

C:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:7:9: Error: Non-nullable variable 'year' must be assigned before it can be used.
  print(year);     // Kompilierfehler: year wurde nicht initialisiert
        ^^^^Code-Sprache: JavaScript (javascript)

Bei nullbaren Variablen ist diese Schreibweise zulässig

void main() {
  int? age;        // Nullbare Variable, Standardwert ist null
  print(age);      // Gibt null aus, ohne Fehler
  
  //age = 18;        // Manuelle Wertzuweisung
  //print(age);      // Gibt 18 aus
}Code-Sprache: JavaScript (javascript)

3. Auf Eigenschaften oder Methoden von Ausdrücken mit nullbarem Typ kann nicht direkt zugegriffen werden. Auch hier gibt es eine Ausnahme: Methoden und Eigenschaften, die nativ von null unterstützt werden (hashCode, toString()), dürfen aufgerufen werden.

void main() {
  String? name; // Nullbarer Typ, Standardwert ist null

  // Fehler: Direkter Zugriff auf length erzeugt einen Fehler
  // print(name.length);

  // Zulässig: Aufruf von nativ von null unterstützten Methoden
  print(name.toString());  // Gibt "null" aus
  print(name.hashCode);    // Gibt eine Ganzzahl aus

  // Korrekt: Verwendung des Null-Sicherheitsoperators
  print(name?.length);     // Gibt null aus, ohne Fehler
}Code-Sprache: PHP (php)

Standardwerte

Nullbare Variablen ohne manuelle Wertzuweisung erhalten automatisch den Standardwert null. Selbst Variablen mit Zahlentyp haben als Standardwert null,

denn in Dart sind Zahlen wie alle anderen Daten im Grunde Objekte.

void main() {
  int? lineCount;
  assert(lineCount == null);
}Code-Sprache: JavaScript (javascript)

Hinweis: Der Prüfaufruf assert() wird in Produktivcode ignoriert; in der Entwicklungsumgebung löst das Programm eine Ausnahme aus, wenn die Bedingung innerhalb von assert(Bedingung) nicht erfüllt ist.

Nach Aktivierung der Null-Sicherheit müssen nicht nullbare Variablen vor der Verwendung initialisiert werden:

int lineCount = 0;

Lokale Variablen müssen nicht in derselben Zeile wie die Deklaration zugewiesen werden, aber die Zuweisung muss vor der ersten Nutzung erfolgen. Der folgende Code ist zulässig, da Dart nachweisen kann, dass lineCount zum Zeitpunkt des Aufrufs von print() stets einen nicht-null-Wert hat:

int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;
}

print(lineCount);

Code-Sprache: Dart (dart)

Hinweis: In obigem Code stellen die Zeilen lineCount = countLines(); und lineCount = 0; nur eine Wertzuweisung dar – das ist noch keine Nutzung. Als Nutzung bezeichnet man das Auslesen des gespeicherten Werts. Der folgende Code erzeugt beispielsweise einen Fehler:

void main() {
	int lineCount;

	print(lineCount);
}
Code-Sprache: Dart (dart)

Kompilierfehler

Top-Level-Variablen und Klassenmember-Variablen unterliegen der verzögerten Initialisierung: Die Zuweisungslogik wird erst ausgeführt, wenn die Variable erstmals verwendet wird.

Variablen mit late-Modifikator

Der Modifikator late hat zwei Anwendungsfälle:

  1. Deklarieren einer nicht nullbaren Variablen, deren Wert erst später zugewiesen wird;
  2. Implementieren einer verzögerten Variableninitialisierung.

In den meisten Fällen erkennt Dart mithilfe der Ablaufanalyse, dass eine nicht nullbare Variable vor der Verwendung einen gültigen nicht-null-Wert erhält. In einigen Fällen schlägt diese Analyse jedoch fehl – am häufigsten bei Top-Level-Variablen und Instanzvariablen. Der Compiler kann in der Regel nicht prüfen, ob diesen Variablen ein Wert zugewiesen wurde und gibt daher automatisch einen Fehler aus.

Wenn Sie sicher sind, dass die Variable vor der Nutzung auf jeden Fall einen Wert erhält, der Compiler dies aber nicht erkennen kann, versehen Sie die Variable mit dem Modifikator late, um den Fehler zu beseitigen:

Im folgenden Beispiel ist description eine globale Variable. Der Compiler kann ggf. nicht prüfen, ob ihr ein Wert zugewiesen wird. Wenn Sie aber sicher sind, dass die Zuweisung stattfindet, verwenden Sie den Modifikator late.

late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}
Code-Sprache: Dart (dart)

Hinweis

Wenn Sie eine mit late markierte Variable deklarieren, ihr aber niemals einen Wert zuweisen, löst der Zugriff auf die Variable einen Laufzeitfehler aus.

Wenn Sie einer mit late markierten Variable direkt einen Anfangswert zuweisen, wird dieser Initialisierungscode nur ausgeführt, wenn die Variable erstmalig aufgerufen wird. Diese verzögerte Initialisierung ist in zwei Szenarien besonders nützlich:

  1. Die Variable wird unter Umständen nie verwendet und die Initialisierung ist rechenintensiv;
  2. Bei der Initialisierung einer Instanzvariable benötigt die Initialisierungslogik Zugriff auf this.

Im folgenden Beispiel wird die rechenintensive Funktion readThermometer() niemals ausgeführt, wenn die Variable temperature im gesamten Programm nicht genutzt wird:

// Die Funktion readThermometer() wird nur aufgerufen, wenn temperature abgefragt wird
late String temperature = readThermometer(); // Verzögerte Initialisierung
Code-Sprache: JavaScript (javascript)

final und const

Wenn Sie verhindern möchten, dass der Wert einer Variable verändert wird, verwenden Sie final oder const. Beide Schlüsselwörter können allein oder zusammen mit einer Typangabe statt var verwendet werden.

  • final-Variablen dürfen genau einmal zugewiesen werden;
  • const-Variablen sind Kompilierzeitkonstanten (eine const-Variable verfügt implizit über die Eigenschaften von final).

Hinweis

Instanzmember-Variablen können mit final markiert werden, nicht jedoch mit const. Für klassenweite Konstanten verwenden Sie static const.

Unten sehen Sie Beispiele für die Definition von final-Variablen:

final name = 'Jack'; // Ohne explizite Typangabe
final String nickname = 'Jason';
Code-Sprache: PHP (php)

Sie können den Wert einer final-Variable nicht ändern – der Compiler gibt direkt einen Fehler aus:

void main() {
	final name = 'Jack'; // Ohne explizite Typangabe
	final String nickname = 'Jason';
	
	name = 'Alice'; // Fehler: Final-Variablen dürfen nur einmal zugewiesen werden
}Code-Sprache: JavaScript (javascript)
C:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:6:2: Error: Can't assign to the final variable 'name'.
        name = 'Alice'; // Fehler: Final-Variablen dürfen nur einmal zugewiesen werdenCode-Sprache: PHP (php)

const dient der Definition von Kompilierzeitkonstanten. Wenn eine Konstante innerhalb einer Klasse definiert wird, ergänzen Sie den Modifikator static zu static const. Bei der Deklaration einer const-Variable muss der zugewiesene Wert bereits zur Kompilierzeit feststehen – beispielsweise Zahlen, String-Literale, andere const-Variablen oder Ergebnisse von Rechenoperationen mit konstanten Zahlen:

const bar = 1000000; // Druckeinheit (Dyn pro Quadratzentimeter)
const double atm = 1.01325 * bar; // Standardatmosphärendruck
Code-Sprache: JavaScript (javascript)

Das Schlüsselwort const eignet sich nicht nur zur Deklaration konstanter Variablen, sondern auch zum Erstellen konstanter Objekte oder zum Definieren von Konstruktoren, die konstante Instanzen erzeugen. Jede beliebige Variable kann ein konstantes Objekt als Wert erhalten.

var foo = const [];
final bar = const [];
const baz = []; // Entspricht const []
Code-Sprache: PHP (php)

In der Zuweisung einer const-Variable kann das Schlüsselwort const weggelassen werden, wie bei der Schreibweise von baz oben gezeigt.

Selbst wenn eine Variable einst ein konstantes Objekt erhalten hat, können Sie ihre Referenz neu zuweisen – solange sie weder final noch const ist:

foo = [1, 2, 3]; // Ursprünglich zeigte foo auf ein leeres konstantes Array, jetzt kann die Referenz gewechselt werden
Code-Sprache: JavaScript (javascript)

const-Variablen lassen sich hingegen nicht neu zuweisen – der Compiler gibt einen Fehler aus:

// Statische Analyse: Code-Fehler
baz = [42]; // Fehler: Konstanten-Variablen dürfen keine neue Zuweisung erhalten
Code-Sprache: JavaScript (javascript)

Bei der Definition von Konstanten können Typabfragen, Typumwandlungen (is, as), Collection-If und Spread-Operatoren (..., ...?) verwendet werden:

void main() {
	const Object i = 3; // i ist eine Konstante vom Typ Object, die einen Integer-Wert speichert
	const list = [i as int]; // Verwendung einer Typumwandlung
	const map = {if (i is int) i: 'int'}; // Verwendung von is-Abfrage und Collection-If
	const set = {if (list is List<int>) ...list}; // Kombination mit Spread-Operator
}Code-Sprache: JavaScript (javascript)

Hinweis

Die Referenz eines mit final markierten Objekts kann nicht verändert werden, aber die internen Felder des Objekts lassen sich bearbeiten. Im Gegensatz dazu sind mit const markierte Objekte samt aller enthaltenen Daten vollständig unveränderlich.

Weitere Informationen zur Erstellung konstanter Collections mit const finden Sie in der Dokumentation zu Listen, Maps und Klassen.

* Platzhaltervariablen

Versionshinweis: Platzhaltervariablen erfordern mindestens Dart-Sprachversion 3.7.

Eine Platzhaltervariable mit dem Namen _ ist eine ungebundene lokale Variable bzw. ein Parameter, der lediglich als Platzhalter fungiert. Wenn sie mit einem Initialisierungsausdruck versehen wird, wird dieser Ausdruck zwar ausgeführt, aber Sie können den gespeicherten Wert nicht auslesen. Innerhalb desselben Gültigkeitsbereichs dürfen mehrere Variablen mit dem Namen _ definiert werden, ohne Namenskonflikte zu erzeugen.

Was ist eine Platzhaltervariable?

In Dart ist der einfache Unterstrich _ die Platzhaltervariable:

  1. Sie dient zum Auffangen von Werten, die nicht weiterverwendet werden sollen und signalisiert dem Compiler: „Ich lese diese Variable nicht, ich brauche sie nicht“;
  2. Der Compiler gibt keine Warnung „Variable definiert, aber nicht verwendet“ aus;
  3. Sie können den Wert von _ nicht auslesen (ein Lesezugriff erzeugt einen Fehler) – sie dient nur als Platzhalter zum Verwerfen von Daten.
// Nur der 1. und 3. Wert werden übernommen, der zweite wird mit _ verworfen
final [a, _, c] = [10, 20, 30];
print(a); // 10
print(c); // 30
// print(_); // Fehler, der Platzhalter kann nicht gelesen werdenCode-Sprache: PHP (php)

Platzhaltervariablen sind unzulässig bei Top-Level-Deklarationen und Klassenmembern, die die private Zugriffsberechtigung von Bibliotheken beeinflussen. Sie dürfen nur innerhalb von Blockgültigkeitsbereichen verwendet werden, siehe folgende Beispiele:

  1. Deklaration lokaler Variablen
void main() {
  var _ = 1;
  int _ = 2;
}
Code-Sprache: JavaScript (javascript)
  1. Schleifenvariablen in for-Schleifen
for (var _ in list) {}
Code-Sprache: PHP (php)
  1. Fangparameter in catch-Blöcken
try {
  throw '!';
} catch (_) {
  print('oops');
}
Code-Sprache: PHP (php)
  1. Generische Typen, generische Funktionsparameter
class T<_> {}
void genericFunction<_>() {}

takeGenericCallback(<_>() => true);
Code-Sprache: JavaScript (javascript)
  1. Funktionsparameter
Foo(_, this._, super._, void _()) {}

list.where((_) => true);

void f(void g(int _, bool _)) {}

typedef T = void Function(String _, String _);Code-Sprache: JavaScript (javascript)

Schreibe einen Kommentar

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