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 late
ci - 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 null
par défaut:
void main() {
String? word;
print(word); // prints null
}
!
L'ajout !
à une variable e
générera une erreur d'exécution si e
est 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é late
peut ê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 word
avant 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 heavyComputation
ne sera appelé qu'une fois x
accessible. De plus, vous pouvez également déclarer un late final
sans initialiseur, ce qui équivaut à n'avoir qu'une late
variable, 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 required
ou 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é.
Never
va ê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, Never
signifie qu'aucun type n'est autorisé et Never
lui - même ne peut pas être instancié.
Rien que Never
dans 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>
.
Never
n'est pas censé être utilisé par les programmeurs en ce qui me concerne.
Never
peuvent être utilisés?