Brak wartości zerowej (domyślnie)
Eksperyment bez wartości zerowej (domyślnie) można obecnie znaleźć na stronie nullsafety.dartpad.dev .
Pamiętaj, że możesz przeczytać pełną specyfikację tutaj i pełną mapę drogową tutaj .
Co domyślnie oznacza wartość zerowa?
void main() {
String word;
print(word); // illegal
word = 'Hello, ';
print(word); // legal
}
Jak widać powyżej, zmienna domyślnie nie ma wartości zerowej oznacza, że każda deklarowana normalnie nie może być null. W konsekwencji każda operacja dostępu do zmiennej przed jej przypisaniem jest nielegalna.
Ponadto przypisywanie nulldo zmiennej nie dopuszczającej wartości zerowej jest również niedozwolone:
void main() {
String word;
word = null; // forbidden
world = 'World!'; // allowed
}
Jak mi to pomaga?
Jeśli zmienna nie ma wartości zerowej , możesz być pewien, że nigdy nie jest null. Z tego powodu nigdy nie musisz tego sprawdzać wcześniej.
int number = 4;
void main() {
if (number == null) return; // redundant
int sum = number + 2; // allowed because number is also non-nullable
}
Zapamiętaj
Pola instancji w klasach należy zainicjować, jeśli nie mają wartości zerowej:
class Foo {
String word; // forbidden
String sentence = 'Hello, World!'; // allowed
}
Zobacz lateponiżej, aby zmodyfikować to zachowanie.
Typy zerowalne ( ?)
Możesz użyć typów zerowalnych , dodając znak zapytania ?do typu zmiennej:
class Foo {
String word; // forbidden
String? sentence; // allowed
}
Pustych zmienna nie musi być zainicjowany zanim zostanie użyty. Jest on inicjowany nulldomyślnie:
void main() {
String? word;
print(word); // prints null
}
!
Dołączanie !do dowolnej zmiennej erzuci runtime error , jeśli ejest zerowy, a inaczej przekształcić go non wartości pustych wartości 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
Słowa kluczowego latemożna użyć do oznaczenia zmiennych, które zostaną zainicjowane później , tj. Nie wtedy, gdy zostaną zadeklarowane, ale kiedy zostaną udostępnione. Oznacza to również, że możemy mieć niepuste pola instancji, które są inicjowane później:
class ExampleState extends State {
late String word; // non-nullable
@override
void initState() {
super.initState();
// print(word) here would throw a runtime error
word = 'Hello';
}
}
Uzyskiwanie dostępu wordprzed jego zainicjowaniem spowoduje wygenerowanie błędu czasu wykonywania.
late final
Zmienne końcowe można teraz także oznaczać późno:
late final int x = heavyComputation();
Tutaj heavyComputationzostanie wywołany tylko raz x. Dodatkowo możesz również zadeklarować opcję „ late finalbez inicjatora”, co jest równoznaczne z posiadaniem tylko latezmiennej, ale można ją przypisać tylko raz.
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
Zauważ, że wszystkie zmienne najwyższego poziomu lub statyczne z inicjatorem będą teraz oceniane late, bez względu na to, czy są final.
required
Dawniej adnotacja ( @required), teraz wbudowana jako modyfikator. Pozwala oznaczyć dowolny nazwany parametr (dla funkcji lub klas) jako required, co powoduje, że nie mają one wartości zerowej:
void allowed({required String word}) => null;
Oznacza to również, że jeśli parametr nie ma wartości zerowej , należy go oznaczyć jako requiredlub mieć wartość domyślną:
void allowed({String word = 'World'}) => null;
void forbidden({int x}) // compile-time error because x can be null (unassigned)
=>
null;
Każdy inny nazwany parametr musi mieć wartość null :
void baz({int? x}) => null;
?[]
?[]Dla operatora indeksu dodano operator o wartości NULL []:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
Zobacz także ten artykuł na temat decyzji dotyczącej składni .
?..
Operator kaskada ma teraz również nowy operator zerowej świadomy: ?...
Powoduje to, że następujące operacje kaskadowe są wykonywane tylko wtedy, gdy odbiorca nie ma wartości null . Dlatego ?..musi być pierwszym operatorem kaskadowym w sekwencji kaskadowej:
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
Aby uniknąć zamieszania: programiści nie muszą się tym martwić. Chcę o tym wspomnieć w celu uzupełnienia.
Neverbędzie typem takim jak poprzednio Null( nienull ) zdefiniowany w dart:core. Obie te klasy nie mogą być rozszerzane, implementowane ani mieszane, więc nie są przeznaczone do użycia.
Zasadniczo Neveroznacza, że żaden typ nie jest dozwolony i Neversama nie może być utworzona.
Nic jednak Neverw ciągu List<Never>spełnia ogólny typ ograniczenie listy, co oznacza, że musi być pusty . List<Null>może jednak zawierać 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)
];
Przykład: kompilator będzie wnioskować List<Never>o pustym const List<T> .
Nevermoim zdaniem nie powinien być używany przez programistów.
Nevermożna je wykorzystać?