Não anulável (por padrão)
Atualmente, a experiência não anulável (por padrão) pode ser encontrada em nullsafety.dartpad.dev .
Lembre-se de que você pode ler as especificações completas aqui e o roteiro completo aqui .
O que significa não anulável por padrão?
void main() {
String word;
print(word); // illegal
word = 'Hello, ';
print(word); // legal
}
Como você pode ver acima, uma variável que não é anulável por padrão significa que toda variável declarada normalmente não pode ser null. Conseqüentemente, qualquer operação que acesse a variável antes de ser atribuída é ilegal.
Além disso, a atribuição nulla uma variável não anulável também não é permitida:
void main() {
String word;
word = null; // forbidden
world = 'World!'; // allowed
}
Como isso me ajuda?
Se uma variável não for anulável , você pode ter certeza de que nunca é null. Por causa disso, você nunca precisa verificar antes.
int number = 4;
void main() {
if (number == null) return; // redundant
int sum = number + 2; // allowed because number is also non-nullable
}
Lembrar
Os campos de instância nas classes devem ser inicializados se não forem anuláveis:
class Foo {
String word; // forbidden
String sentence = 'Hello, World!'; // allowed
}
Veja lateabaixo para modificar esse comportamento.
Tipos anuláveis ( ?)
Você pode usar tipos anuláveis anexando um ponto de interrogação ?a um tipo de variável:
class Foo {
String word; // forbidden
String? sentence; // allowed
}
Uma variável anulável não precisa ser inicializada antes de poder ser usada. É inicializado como nullpadrão:
void main() {
String? word;
print(word); // prints null
}
!
O acréscimo !a qualquer variável egerará um erro de tempo de execução se efor nulo e o converterá em um valor não nulov .
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
A palavra-chave latepode ser usada para marcar variáveis que serão inicializadas posteriormente , ou seja, não quando forem declaradas, mas quando forem acessadas. Isso também significa que podemos ter campos de instância não anuláveis que são inicializados posteriormente:
class ExampleState extends State {
late String word; // non-nullable
@override
void initState() {
super.initState();
// print(word) here would throw a runtime error
word = 'Hello';
}
}
O acesso wordantes de ser inicializado gera um erro de tempo de execução.
late final
As variáveis finais agora também podem ser marcadas com atraso:
late final int x = heavyComputation();
Aqui heavyComputationserá chamado apenas quando xfor acessado. Além disso, você também pode declarar a late finalsem um inicializador, que é o mesmo que ter apenas uma latevariável, mas só pode ser atribuída uma vez.
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
Observe que todas as variáveis estáticas ou de nível superior com um inicializador agora serão avaliadas , independentemente de serem .latefinal
required
Anteriormente uma anotação ( @required), agora incorporada como um modificador. Permite marcar qualquer parâmetro nomeado (para funções ou classes) como required, o que os torna não anuláveis:
void allowed({required String word}) => null;
Isso também significa que, se um parâmetro for não nulo , ele precisa ser marcado como requiredou ter um valor padrão:
void allowed({String word = 'World'}) => null;
void forbidden({int x}) // compile-time error because x can be null (unassigned)
=>
null;
Qualquer outro parâmetro nomeado deve ser anulável :
void baz({int? x}) => null;
?[]
O ?[]operador com reconhecimento de nulo foi adicionado ao operador de índice []:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
Consulte também este artigo sobre a decisão de sintaxe .
?..
O operador cascata agora também tem um novo operador ciente nulo: ?...
Isso faz com que as seguintes operações em cascata sejam executadas apenas se o destinatário não for nulo . Portanto, ?..ele deve ser o primeiro operador em cascata em uma sequência em 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
Para evitar confusão: isso não é algo com que os desenvolvedores tenham que se preocupar. Eu quero mencionar isso por uma questão de perfeição.
Neverserá um tipo como o anteriormente existente Null( nãonull ) definido em dart:core. Ambas as classes não podem ser estendidas, implementadas ou combinadas; portanto, elas não devem ser usadas.
Essencialmente, Neversignifica que nenhum tipo é permitido e Nevernão pode ser instanciado.
Nada, mas Neverem um List<Never>satisfaz o genérico tipo de restrição da lista, o que significa que tem que ser vazio . List<Null>, no entanto, pode conter 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)
];
Exemplo: o compilador inferirá List<Never>para um vazio const List<T> .
Nevernão deve ser usado pelos programadores no que me diz respeito.
Neverpode ser usado?