Non annullabile (per impostazione predefinita)
L' esperimento non annullabile (per impostazione predefinita) è attualmente disponibile su nullsafety.dartpad.dev .
Tieni presente che puoi leggere le specifiche complete qui e la roadmap completa qui .
Cosa significa non annullabile per impostazione predefinita?
void main() {
String word;
print(word); // illegal
word = 'Hello, ';
print(word); // legal
}
Come puoi vedere sopra, una variabile essendo nulla per impostazione predefinita significa che ogni variabile dichiarata normalmente non può essere null
. Di conseguenza, qualsiasi operazione che accede alla variabile prima che sia stata assegnata è illegale.
Inoltre, null
non è consentita l' assegnazione a una variabile non nullable:
void main() {
String word;
word = null; // forbidden
world = 'World!'; // allowed
}
In che modo mi aiuta?
Se una variabile è non annullabile , puoi essere sicuro che non lo sia mai null
. Per questo motivo, non è mai necessario verificarlo in anticipo.
int number = 4;
void main() {
if (number == null) return; // redundant
int sum = number + 2; // allowed because number is also non-nullable
}
Ricorda
I campi dell'istanza nelle classi devono essere inizializzati se non sono annullabili:
class Foo {
String word; // forbidden
String sentence = 'Hello, World!'; // allowed
}
Vedi late
sotto per modificare questo comportamento.
Tipi nullabili ( ?
)
È possibile utilizzare i tipi nullable aggiungendo un punto interrogativo ?
a un tipo variabile:
class Foo {
String word; // forbidden
String? sentence; // allowed
}
Non è necessario inizializzare una variabile nullable prima di poter essere utilizzata. È inizializzato come null
predefinito:
void main() {
String? word;
print(word); // prints null
}
!
L'aggiunta !
a qualsiasi variabile e
genererà un errore di runtime se e
è nullo e in caso contrario lo convertirà in un valore non annullabilev
.
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
La parola chiave late
può essere utilizzata per contrassegnare le variabili che verranno inizializzate in un secondo momento , cioè non quando vengono dichiarate ma quando vi si accede. Ciò significa anche che possiamo avere campi di istanza non annullabili che vengono inizializzati in seguito:
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'accesso word
prima che sia inizializzato genererà un errore di runtime.
late final
Le variabili finali ora possono anche essere contrassegnate in ritardo:
late final int x = heavyComputation();
Qui heavyComputation
verrà chiamato solo una volta effettuato l' x
accesso. Inoltre, puoi anche dichiarare a late final
senza un inizializzatore, che è lo stesso di avere solo una late
variabile, ma può essere assegnato solo una volta.
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
Nota che ora verranno valutate tutte le variabili di livello superiore o statiche con un inizializzatore late
, indipendentemente dal fatto che lo siano final
.
required
Precedentemente un'annotazione ( @required
), ora integrata come modificatore. Consente di contrassegnare qualsiasi parametro denominato (per funzioni o classi) come required
, il che li rende non annullabili:
void allowed({required String word}) => null;
Ciò significa anche che se un parametro deve essere non annullabile , deve essere contrassegnato come required
o avere un valore predefinito:
void allowed({String word = 'World'}) => null;
void forbidden({int x}) // compile-time error because x can be null (unassigned)
=>
null;
Qualsiasi altro parametro denominato deve essere nullable :
void baz({int? x}) => null;
?[]
L' ?[]
operatore nullo è stato aggiunto per l'operatore indice []
:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
Vedi anche questo articolo sulla decisione di sintassi .
?..
L'operatore a cascata ha ora anche un nuovo operatore di nulla consapevoli: ?..
.
Fa sì che le seguenti operazioni in cascata vengano eseguite solo se il destinatario non è nullo . Pertanto, ?..
deve essere il primo operatore a cascata in una sequenza a cascata:
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
Per evitare confusione: questo non è qualcosa di cui gli sviluppatori devono preoccuparsi. Voglio menzionarlo per completezza.
Never
sarà un tipo come quello precedentemente esistente Null
( nonnull
) definito in dart:core
. Entrambe queste classi non possono essere estese, implementate o mescolate, quindi non sono pensate per essere utilizzate.
In sostanza, Never
significa che nessun tipo è consentito e di per Never
sé non può essere istanziato.
Nient'altro che Never
in una List<Never>
soddisfa il vincolo di tipo generico dell'elenco, il che significa che deve essere vuoto . List<Null>
, tuttavia, può contenere 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)
];
Esempio: il compilatore inferirà List<Never>
per un vuoto const List<T>
.
Never
non dovrebbe essere utilizzato dai programmatori per quanto mi riguarda.
Never
può essere utilizzato?