Was ist Nullability in Dart (standardmäßig nicht nullbar)?

27

Ich habe von der neuen Dart-Null-Sicherheitssprachenfunktion gehört, derzeit das " nicht nullbare Experiment ". Es soll standardmäßig nicht nullbar einführen .

Die Funktionsspezifikation finden Sie hier und die Sprache GitHub hier .

Wie funktioniert es und wo kann ich es ausprobieren?

CreativeCreatorormaybenot
quelle

Antworten:

49

Nicht nullbar (standardmäßig)

Das nicht standardmäßige Experiment (standardmäßig) finden Sie derzeit unter nullsafety.dartpad.dev .
Beachten Sie, dass Sie die vollständige Spezifikation hier und die vollständige Roadmap hier lesen können .


Was bedeutet standardmäßig nicht nullbar?

void main() {
  String word;
  print(word); // illegal

  word = 'Hello, ';
  print(word); // legal
}

Wie Sie oben sehen können, bedeutet eine Variable, die standardmäßig nicht nullwertfähig ist , dass jede normal deklarierte Variable nicht sein kann null. Folglich ist jede Operation, die auf die Variable zugreift, bevor sie zugewiesen wurde, unzulässig.
Darüber hinaus ist das Zuweisen nullzu einer nicht nullbaren Variablen nicht zulässig:

void main() {
  String word;

  word = null; // forbidden
  world = 'World!'; // allowed
}

Wie hilft mir das?

Wenn eine Variable nicht nullwertfähig ist , können Sie sicher sein, dass dies niemals der Fall ist null. Aus diesem Grund müssen Sie es nie vorher überprüfen.

int number = 4;

void main() {
  if (number == null) return; // redundant

  int sum = number + 2; // allowed because number is also non-nullable
}

Merken

Instanzfelder in Klassen müssen initialisiert werden, wenn sie nicht nullwertfähig sind:

class Foo {
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed
}

Siehe lateunten, um dieses Verhalten zu ändern.

Nullable types ( ?)

Sie können nullfähige Typen verwenden, indem Sie ?einem Variablentyp ein Fragezeichen hinzufügen :

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

Eine nullfähige Variable muss nicht initialisiert werden, bevor sie verwendet werden kann. Es wird nullstandardmäßig initialisiert :

void main() {
  String? word;

  print(word); // prints null
}

!

Das Anhängen !an eine Variable elöst einen Laufzeitfehler aus, wenn dieser enull ist, und konvertiert ihn andernfalls in einen nicht nullbaren Wert v.

void main() {
  int? e = 5;
  int v = e!; // v is non-nullable; would throw an error if e were null

  String? word;
  print(word!); // throws runtime error if word is null

  print(null!); // throws runtime error
}

late

Das Schlüsselwort latekann verwendet werden, um Variablen zu markieren, die später initialisiert werden , dh nicht, wenn sie deklariert werden, sondern wenn auf sie zugegriffen wird. Dies bedeutet auch, dass wir nicht nullbar haben können können , die später initialisiert werden:

class ExampleState extends State {
  late String word; // non-nullable

  @override
  void initState() {
    super.initState();

    // print(word) here would throw a runtime error
    word = 'Hello';
  }
}

Zugriff word vor der Initialisierung führt zu einem Laufzeitfehler.

late final

Endgültige Variablen können jetzt auch spät markiert werden:

late final int x = heavyComputation();

Hier heavyComputationwird nur einmal aufgerufen, wenn darauf xzugegriffen wird. Darüber hinaus können Sie a auch late finalohne Initialisierer deklarieren , was mit a identisch istlate Variablen, kann jedoch nur einmal zugewiesen werden.

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden

Beachten Sie, dass jetzt alle Variablen der obersten Ebene oder statischen Variablen mit einem Initialisierer ausgewertet werden late, unabhängig davon, ob dies der Fall ist final.

required

Früher eine Annotation ( @required), jetzt als Modifikator integriert. Sie können jeden benannten Parameter (für Funktionen oder Klassen) als markieren required, wodurch sie nicht nullbar sind:

void allowed({required String word}) => null;

Dies bedeutet auch, dass ein Parameter, wenn er nicht nullwertfähig sein soll , als requiredStandardwert markiert werden oder einen Standardwert haben muss:

void allowed({String word = 'World'}) => null;

void forbidden({int x}) // compile-time error because x can be null (unassigned)
    =>
    null;

Jeder andere benannte Parameter muss nullwertfähig sein :

void baz({int? x}) => null;

?[]

Der nullbewusste ?[]Operator wurde für den Indexoperator hinzugefügt []:

void main() {
  List<int>? list = [1, 2, 3];

  int? x = list?[0]; // 1
}

Siehe auch diesen Artikel über die Syntaxentscheidung .

?..

Der Kaskadenoperator hat jetzt auch einen neuen nullbewussten Operator : ?...

Die folgenden Kaskadenoperationen werden nur ausgeführt, wenn der Empfänger nicht null ist . Daher muss der ?..der erste Kaskadenoperator in einer Kaskadensequenz sein:

void main() {
  Path? path;

  // Will not do anything if path is null.
  path
    ?..moveTo(3, 4)
    ..lineTo(4, 3);

  // This is a noop.
  (null as List)
    ?..add(4)
    ..add(2)
    ..add(0);
}

Never

Um Verwirrung zu vermeiden: Dies ist nichts, worüber sich Entwickler Sorgen machen müssen. Ich möchte es der Vollständigkeit halber erwähnen.

Neverwird ein Typ sein, wie er zuvor in Null( nichtnull ) definiert wurdedart:core . Diese beiden Klassen können nicht erweitert, implementiert oder gemischt werden, sodass sie nicht zur Verwendung vorgesehen sind.

Bedeutet im Wesentlichen, Neverdass kein Typ zulässig ist und Neverselbst nicht instanziiert werden kann.
Nichts aber Neverin einem List<Never>erfüllt die gattungsgemäße Einschränkung der Liste, was bedeutet , dass es sein muss leer . List<Null>kann jedoch enthalten null:

// Only valid state: []
final neverList = <Never>[
  // Any value but Never here will be an error.
  5, // error
  null, // error

  Never, // not a value (compile-time error)
];

// Can contain null: [null]
final nullList = <Null>[
  // Any value but Null will be an error.
  5, // error
  null, // allowed

  Never, // not a value (compile-time error)
  Null, // not a value (compile-time error)
];

Beispiel: Der Compiler wird auf List<Never>ein Leerzeichen schließen const List<T> .
Neversoll für mich nicht von Programmierern benutzt werden.

CreativeCreatorormaybenot
quelle
5
Können Sie einige Szenarien angeben, in denen Neververwendet werden kann?
Ramses Aldama
2
Wir haben uns tatsächlich entschieden, "? []" Für den nullbewussten Indexoperator anstelle von "?. []" Zu verwenden. Letzteres ist grammatikalisch etwas komplexer, aber es ist das, was Benutzer wollen.
Munificent
@ RamsesAldama Ich habe etwas hinzugefügt. Die Spezifikation, die ich verlinkt habe, erwähnt mehr.
creativecreatorormaybenot
@munificent Die Spezifikation und das Experiment sind dann noch veraltet; Vielen Dank für den Hinweis.
creativecreatorormaybenot
1
Es sollte darauf hingewiesen werden, dass late finaleine Mitglieds- oder Instanzvariable nur zur Laufzeit überprüft wird. Aufgrund des Halteproblems ist es nicht möglich, dies zur Entwicklungszeit oder zur Kompilierungszeit zu überprüfen. Sie erhalten also keine IDE-Hilfe.
Graham