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?late final
em um membro ou variável de instância, apenas verifica em tempo de execução. Não é possível verificar isso no momento do desenvolvimento ou no tempo de compilação devido ao problema de interrupção. Portanto, você não receberá ajuda do IDE.