La verificación nula no provoca promoción de tipo en Dart
Estoy actualizando un paquete personal basado en el marco Flutter. Noté aquí en el código fuente del widget Flutter Text que hay una verificación nula:
if (textSpan != null) {
properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
}
Sin embargo, textSpan!
todavía está usando el !
operador. ¿No debería textSpan
promocionarse a un tipo que no admite valores NULL sin tener que utilizar el !
operador? Sin embargo, al intentar eliminar el operador se produce el siguiente error:
An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.
Aquí hay un ejemplo autónomo:
class MyClass {
String? _myString;
String get myString {
if (_myString == null) {
return '';
}
return _myString; // <-- error here
}
}
Recibo un error en tiempo de compilación:
Error: ¿Un valor de tipo 'Cadena?' no se puede devolver desde la función 'myString' porque tiene un tipo de retorno de 'String'.
O si intento obtenerlo, _mySting.length
aparece el siguiente error:
No se puede acceder incondicionalmente a la propiedad 'longitud' porque el receptor puede ser 'nulo'.
Pensé que hacer la verificación nula promovería _myString
a un tipo que no admite valores NULL. ¿Por qué no?
Mi pregunta se resolvió en GitHub, así que publicaré una respuesta a continuación.
El ingeniero de Dart, Erik Ernst, dice en GitHub :
La promoción de tipo solo es aplicable a variables locales. ... La promoción de una variable de instancia no es buena, porque podría ser anulada por un captador que ejecuta un cálculo y devuelve un objeto diferente cada vez que se invoca. Cfr. dart-lang/language#1188 para discusiones sobre un mecanismo similar a la promoción de tipos pero basado en comprobaciones dinámicas, con algunos enlaces a discusiones relacionadas.
Entonces la promoción de tipo local funciona:
String myMethod(String? myString) {
if (myString == null) {
return '';
}
return myString;
}
Pero las variables de instancia que no lo son final
y que no son privadas no se promocionan. (Antes de Dart 3.2, las variables no locales nunca se promocionaban). Para eso, debe decirle manualmente a Dart que está seguro de que la variable de instancia no es nula en este caso usando el !
operador:
class MyClass {
String? _myString;
String myMethod() {
if (_myString == null) {
return '';
}
return _myString!;
}
}
El error:
Digamos que este es tu código y estás haciendo una verificación nula en la variable de instancia y aún ves un error:
class Foo {
int? x;
double toDouble() {
if (x != null) return x.toDouble(); // <-- Error
return -1;
}
}
El método 'toDouble' no se puede invocar incondicionalmente porque el receptor puede ser 'nulo'.
El error que ve en un código como este se debe a que los captadores no se promocionan a sus contrapartes que no aceptan valores NULL . Hablemos del por qué.
Motivo del error:
Digamos que hay una clase Bar
que extiende Foo
y anula x
el campo y se implementa de esta manera:
class Bar extends Foo {
@override
int? get x => (++_count).isOdd ? 1 : null;
int _count = 0;
}
Ahora, si lo haces
Bar().toDouble();
Se habría topado con un error nulo en tiempo de ejecución, razón por la cual la promoción de tipo getters está prohibida.
Soluciones:
Necesitamos descartar la nulidad de int?
. Generalmente hay 3 formas de hacer esto.
Usar variable local (recomendado)
double toDouble() { final x = this.x; // <-- Use a local variable if (x != null) return x.toDouble(); return -1; }
Usar
?.
con??
double toDouble() { return x?.toDouble() ?? -1; // Provide a default value }
Utilice el operador de afirmación nula (!)
Sólo debes usar esta solución cuando estés 100% seguro de que la variable (
x
) nunca seránull
.double toDouble() { return x!.toDouble(); // Null assertion operator }