La verificación nula no provoca promoción de tipo en Dart

Resuelto Suragch asked hace 4 años • 2 respuestas

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 textSpanpromocionarse 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.lengthaparece 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 _myStringa un tipo que no admite valores NULL. ¿Por qué no?

Mi pregunta se resolvió en GitHub, así que publicaré una respuesta a continuación.

Suragch avatar Nov 27 '20 17:11 Suragch
Aceptado

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 finaly 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!;
  }
}
Suragch avatar Nov 27 '2020 10:11 Suragch

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 Barque extiende Fooy anula xel 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
    }
    
CopsOnRoad avatar Apr 04 '2021 13:04 CopsOnRoad