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?

creativecreatorormaybenot
fonte

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.

creativecreatorormaybenot
fonte
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