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.
Neverse pueden usar?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.