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 null
zu 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 late
unten, 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 null
standardmäßig initialisiert :
void main() {
String? word;
print(word); // prints null
}
!
Das Anhängen !
an eine Variable e
löst einen Laufzeitfehler aus, wenn dieser e
null 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 late
kann 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 heavyComputation
wird nur einmal aufgerufen, wenn darauf x
zugegriffen wird. Darüber hinaus können Sie a auch late final
ohne 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 required
Standardwert 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.
Never
wird 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, Never
dass kein Typ zulässig ist und Never
selbst nicht instanziiert werden kann.
Nichts aber Never
in 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>
.
Never
soll für mich nicht von Programmierern benutzt werden.
Never
verwendet werden kann?late final
eine 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.