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?late final
sur 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.