O que é anulabilidade no dardo (não anulável por padrão)?


27

Ouvi falar do novo recurso da linguagem de segurança nula do Dart, atualmente o " experimento 'não anulável' ". É suposto introduzir não anulável por padrão .

A especificação do recurso pode ser encontrada aqui e o problema do idioma GitHub aqui .

Como funciona e onde posso testá-lo?

Respostas:


49

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.


5
Você pode dar alguns cenários onde Neverpode ser usado?
Ramses Aldama

2
Na verdade, decidimos usar "? []" Para o operador de índice com reconhecimento de nulo em vez de "?. []". O último é um pouco mais complexo gramaticalmente, mas é o que os usuários desejam.
munificent

@RamsesAldama Adicionei algo. A especificação que eu vinculei menciona mais.
creativecreatorormaybenot

@munificent As especificações e o experimento ainda estão desatualizados; obrigado por apontar.
creativecreatorormaybenot

11
Deve-se salientar que, late finalem 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.
Graham
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.