Co to jest Nullability w Dart (domyślnie Nullable)?

27

Słyszałem o nowej funkcji bezpiecznego języka Dart null, która jest obecnie „eksperymentem bez możliwości zerowania ”. Ma domyślnie wprowadzać wartość inną niż zerowa .

Specyfikację funkcji można znaleźć tutaj, a problem języka GitHub tutaj .

Jak to działa i gdzie mogę go wypróbować?

creativecreatorormaybenot
źródło

Odpowiedzi:

49

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.

creativecreatorormaybenot
źródło
5
Czy możesz podać kilka scenariuszy, w których Nevermożna je wykorzystać?
Ramzes Aldama,
2
W rzeczywistości zdecydowaliśmy się użyć „? []” Dla operatora indeksu świadomego zerowego zamiast „?. []”. Ta ostatnia jest nieco bardziej złożona gramatycznie, ale tego właśnie chcą użytkownicy.
wspaniały
@RamsesAldama Coś dodałem. Specyfikacja, do której odsyłam, wspomina więcej.
creativecreatorormaybenot
@munificent Specyfikacja i eksperyment są nadal nieaktualne; dziękuję za zwrócenie uwagi.
creativecreatorormaybenot
1
Należy zauważyć, że late finalw zmiennej członka lub instancji sprawdza tylko w czasie wykonywania. Nie można tego sprawdzić w czasie programowania lub kompilacji z powodu problemu zatrzymania. Więc nie otrzymasz pomocy IDE.
Graham