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 null
a 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 late
abaixo 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 null
padrão:
void main() {
String? word;
print(word); // prints null
}
!
O acréscimo !
a qualquer variável e
gerará um erro de tempo de execução se e
for 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 late
pode 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 word
antes 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 heavyComputation
será chamado apenas quando x
for acessado. Além disso, você também pode declarar a late final
sem um inicializador, que é o mesmo que ter apenas uma late
variá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 .late
final
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 required
ou 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.
Never
será 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, Never
significa que nenhum tipo é permitido e Never
não pode ser instanciado.
Nada, mas Never
em 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>
.
Never
não deve ser usado pelos programadores no que me diz respeito.
Never
pode ser usado?