¿Qué es la anulabilidad en Dart (no anulable por defecto)?

27

He oído hablar de la nueva función de lenguaje de seguridad nulo de Dart, actualmente el " experimento 'no anulable' ". Se supone que introduce no anulable por defecto .

La especificación de características se puede encontrar aquí y el problema de idioma GitHub aquí .

¿Cómo funciona y dónde puedo probarlo?

creativecreatorormaybenot
fuente

Respuestas:

49

No anulable (por defecto)

El experimento no anulable (por defecto) se puede encontrar actualmente en nullsafety.dartpad.dev .
Tenga en cuenta que puede leer la especificación completa aquí y la hoja de ruta completa aquí .


¿Qué significa no anulable por defecto?

void main() {
  String word;
  print(word); // illegal

  word = 'Hello, ';
  print(word); // legal
}

Como puede ver arriba, una variable que no es anulable por defecto significa que cada variable que se declara normalmente no puede serlo null. En consecuencia, cualquier operación que acceda a la variable antes de que se le haya asignado es ilegal.
Además, la asignación nulla una variable no anulable tampoco está permitida:

void main() {
  String word;

  word = null; // forbidden
  world = 'World!'; // allowed
}

¿Cómo esto me ayuda?

Si una variable no es anulable , puede estar seguro de que nunca lo es null. Debido a eso, nunca necesita verificarlo de antemano.

int number = 4;

void main() {
  if (number == null) return; // redundant

  int sum = number + 2; // allowed because number is also non-nullable
}

Recuerda

Los campos de instancia en las clases deben inicializarse si no son anulables:

class Foo {
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed
}

Ver late continuación para modificar este comportamiento.

Tipos anulables (? )

Puede usar tipos anulables agregando un signo de interrogación ?a un tipo variable:

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

No es necesario inicializar una variable anulable antes de poder usarla. Se inicializa como nullpor defecto:

void main() {
  String? word;

  print(word); // prints null
}

!

Agregar !a cualquier variable earrojará un error de tiempo de ejecución si ees nulo y, de lo contrario, lo convertirá en un valor no anulablev .

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

La palabra clave latese puede usar para marcar variables que se inicializarán más adelante , es decir, no cuando se declaran sino cuando se accede a ellas. Esto también significa que podemos tener campos de instancia no anulables que se inicializan más tarde:

class ExampleState extends State {
  late String word; // non-nullable

  @override
  void initState() {
    super.initState();

    // print(word) here would throw a runtime error
    word = 'Hello';
  }
}

Accediendo word antes de que se inicialice arrojará un error de tiempo de ejecución.

late final

Las variables finales ahora también se pueden marcar tarde:

late final int x = heavyComputation();

Aquí heavyComputationsolo se llamará una vez que xse acceda. Además, también puede declarar un late finalsin inicializador, que es lo mismo que tener solo una latevariable, pero solo se puede asignar una vez.

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden

Tenga en cuenta que todas las variables de nivel superior o estáticas con un inicializador ahora se evaluarán late, sin importar si lo son final.

required

Anteriormente una anotación ( @required), ahora incorporada como modificador. Permite marcar cualquier parámetro con nombre (para funciones o clases) comorequired , lo que los hace no anulables:

void allowed({required String word}) => null;

Esto también significa que si un parámetro no puede ser anulado , debe marcarse comorequired o tener un valor predeterminado:

void allowed({String word = 'World'}) => null;

void forbidden({int x}) // compile-time error because x can be null (unassigned)
    =>
    null;

Cualquier otro parámetro con nombre debe ser anulable :

void baz({int? x}) => null;

?[]

Se ?[]agregó el operador nulo para el operador de índice []:

void main() {
  List<int>? list = [1, 2, 3];

  int? x = list?[0]; // 1
}

Consulte también este artículo sobre la decisión de sintaxis .

?..

El operador en cascada ahora también tiene un nuevo operador con reconocimiento nulo: ?.. .

Hace que las siguientes operaciones en cascada solo se ejecuten si el destinatario no es nulo . Por lo tanto, ?..debe ser el primer operador en cascada en una secuencia en cascada:

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 confusiones: esto no es algo de lo que los desarrolladores tengan que preocuparse. Quiero mencionarlo en aras de la integridad.

Neverva a ser un tipo como el previamente existente Null( nonull ) definido endart:core . Ambas clases no se pueden ampliar, implementar o mezclar, por lo que no están destinadas a ser utilizadas.

Básicamente, Neversignifica que no se permite ningún tipo y Neverque no se puede crear una instancia.
Nada más que Neveren List<Never>satisface la restricción de tipo genérico de la lista, lo que significa que tiene que estar vacío . List<Null>, sin embargo, puede contener 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)
];

Ejemplo: el compilador inferirá List<Never>para un vacío const List<T> .
Neverno se supone que sea utilizado por los programadores en lo que a mí respecta.

creativecreatorormaybenot
fuente
55
¿Puedes dar algunos escenarios donde Neverse pueden usar?
Ramses Aldama
2
De hecho, hemos decidido usar "? []" Para el operador de índice nulo en lugar de "?. []". Este último es un poco más complejo gramaticalmente, pero es lo que los usuarios quieren.
munificente
@RamsesAldama He agregado algo. La especificación a la que he vinculado menciona más.
creativecreatorormaybenot
@munificent La especificación y el experimento aún están obsoletos; gracias por señalar
creativecreatorormaybenot
1
Cabe señalar que late finalen una variable miembro o instancia solo se verifica en tiempo de ejecución. No es posible verificar eso en el momento del desarrollo o en el momento de la compilación debido al problema de detención. Por lo tanto, no obtendrá ayuda de IDE con él.
Graham