Qu'est-ce que la nullité dans Dart (non nullable par défaut)?

27

J'ai entendu parler de la nouvelle fonctionnalité du langage de sécurité nul de Dart, actuellement " l'expérience " non nullable ". Il est censé introduire par défaut non nullable .

La spécification de fonctionnalité peut être trouvée ici et le problème de langue GitHub ici .

Comment ça marche et où puis-je l'essayer?

creativecreatorormaybenot
la source

Réponses:

49

Non nullable (par défaut)

L' expérience non nullable (par défaut) se trouve actuellement sur nullsafety.dartpad.dev .
Gardez à l'esprit que vous pouvez lire les spécifications complètes ici et la feuille de route complète ici .


Que signifie non-nullable par défaut?

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

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

Comme vous pouvez le voir ci-dessus, une variable étant non nullable par défaut signifie que chaque variable qui est déclarée normalement ne peut pas l' être null. Par conséquent, toute opération accédant à la variable avant son affectation est illégale.
De plus, l'attribution nullà une variable non nullable n'est pas non plus autorisée:

void main() {
  String word;

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

Comment cela m'aide-t-il?

Si une variable n'est pas nullable , vous pouvez être sûr qu'elle ne l'est jamais null. Pour cette raison, vous n'avez jamais besoin de le vérifier à l'avance.

int number = 4;

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

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

Rappelles toi

Les champs d'instance dans les classes doivent être initialisés s'ils ne peuvent pas être annulés:

class Foo {
  String word; // forbidden

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

Voir lateci - dessous pour modifier ce comportement.

Types nullables ( ?)

Vous pouvez utiliser des types nullables en ajoutant un point d'interrogation ?à un type de variable:

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

Une variable nullable n'a pas besoin d'être initialisée avant de pouvoir être utilisée. Il est initialisé comme nullpar défaut:

void main() {
  String? word;

  print(word); // prints null
}

!

L'ajout !à une variable egénérera une erreur d'exécution si eest nul et sinon le convertira en une valeur non nulle 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

Le mot-clé latepeut être utilisé pour marquer des variables qui seront initialisées ultérieurement , c'est-à-dire non pas quand elles sont déclarées mais quand elles sont accédées. Cela signifie également que nous pouvons avoir des champs d'instance non nullables qui sont initialisés plus tard:

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

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

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

L'accès wordavant son initialisation générera une erreur d'exécution.

late final

Les variables finales peuvent désormais également être marquées tardivement:

late final int x = heavyComputation();

Ici heavyComputationne sera appelé qu'une fois xaccessible. De plus, vous pouvez également déclarer un late finalsans initialiseur, ce qui équivaut à n'avoir qu'une latevariable, mais il ne peut être attribué qu'une seule fois.

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

Notez que toutes les variables de niveau supérieur ou statiques avec un initialiseur seront désormais évaluées late, peu importe si elles le sont final.

required

Anciennement une annotation ( @required), désormais intégrée comme modificateur. Il permet de marquer tout paramètre nommé (pour les fonctions ou les classes) comme required, ce qui les rend non nullables:

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

Cela signifie également que si un paramètre ne doit pas être nullable , il doit être marqué comme requiredou avoir une valeur par défaut:

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

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

Tout autre paramètre nommé doit être nullable :

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

?[]

L' ?[]opérateur de reconnaissance null a été ajouté pour l'opérateur d'index []:

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

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

Voir également cet article sur la décision de syntaxe .

?..

L'opérateur en cascade maintenant dispose également d' un nouvel opérateur courant nul: ?...

Il entraîne l'exécution des opérations en cascade suivantes uniquement si le destinataire n'est pas nul . Par conséquent, le ?..doit être le premier opérateur en cascade dans une séquence en cascade:

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

Pour éviter toute confusion: ce n'est pas quelque chose dont les développeurs doivent s'inquiéter. Je tiens à le mentionner par souci d'exhaustivité.

Neverva être un type comme celui qui existait précédemment Null( nonnull ) défini dans dart:core. Ces deux classes ne peuvent pas être étendues, implémentées ou mélangées, elles ne sont donc pas destinées à être utilisées.

Essentiellement, Neversignifie qu'aucun type n'est autorisé et Neverlui - même ne peut pas être instancié.
Rien que Neverdans un ne List<Never>satisfait la contrainte de type générique de la liste, ce qui signifie qu'elle doit être vide . List<Null>, cependant, peut contenir 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)
];

Exemple: le compilateur déduira List<Never>pour un vide const List<T> .
Nevern'est pas censé être utilisé par les programmeurs en ce qui me concerne.

creativecreatorormaybenot
la source
5
Pouvez-vous donner quelques scénarios où Neverpeuvent être utilisés?
Ramses Aldama
2
Nous avons en fait décidé d'utiliser "? []" Pour l'opérateur d'index à conscience nulle au lieu de "?. []". Ce dernier est un peu plus complexe grammaticalement, mais c'est ce que veulent les utilisateurs.
Munificent
@RamsesAldama J'ai ajouté quelque chose. La spécification que j'ai liée à mentionne plus.
creativecreatorormaybenot
@munificent La spécification et l'expérience sont alors obsolètes; merci d'avoir souligné.
creativecreatorormaybenot
1
Il convient de noter que late finalsur un membre ou une variable d'instance vérifie uniquement au moment de l'exécution. Il n'est pas possible de vérifier cela au moment du développement ou de la compilation en raison du problème d'arrêt. Vous n'obtiendrez donc pas d'aide IDE.
Graham