Saltar al contenido principal

Variables

Aprende sobre las variables en Dart.

Aquí tienes un ejemplo de cómo crear una variable e inicializarla:

dart
var name = 'Bob';

Las variables almacenan referencias. La variable llamada name contiene una referencia a un objeto String con un valor de "Bob".

Se infiere que el tipo de la variable name es String, pero puedes cambiar ese tipo especificándolo. Si un objeto no está restringido a un solo tipo, especifica el tipo Object (o dynamic si es necesario).

dart
Object name = 'Bob';

Otra opción es declarar explícitamente el tipo que se inferiría:

dart
String name = 'Bob';

Null safety

#

El lenguaje Dart impone un null safety seguro (sound null safety).

Null safety evita un error que resulta del acceso no intencionado a variables establecidas en null. El error se denomina error de desreferenciación nula (null dereference error). Un error de desreferenciación nula ocurre cuando accedes a una propiedad o llamas a un método en una expresión que se evalúa como null. Una excepción a esta regla es cuando null admite la propiedad o el método, como toString() o hashCode. Con null safety, el compilador de Dart detecta estos errores potenciales en tiempo de compilación.

Por ejemplo, supón que deseas encontrar el valor absoluto de una variable int i. Si i es null, llamar a i.abs() provoca un error de desreferenciación nula. En otros lenguajes, intentar esto podría provocar un error en tiempo de ejecución. El compilador de Dart evita este error al prohibir estas acciones.

Null safety introduce tres cambios clave:

  1. Cuando especificas un tipo para una variable, parámetro u otro componente relevante, puedes controlar si el tipo permite null. Para habilitar la nulabilidad, agregas un ? al final de la declaración de tipo.

    dart
    String? name  // Nullable type. Can be `null` or string.
    
    String name   // Non-nullable type. Cannot be `null` but can be string.
    
  2. Debes inicializar las variables antes de usarlas. Las variables anulables tienen el valor predeterminado null, por lo que están inicializadas por defecto. Dart no establece valores iniciales para tipos no anulables. Te obliga a establecer un valor inicial. Dart no te permite observar una variable no inicializada. Esto evita que accedas a propiedades o llames a métodos donde el tipo del receptor puede ser null pero null no admite el método o la propiedad utilizados.

  3. No puedes acceder a propiedades ni llamar a métodos en una expresión con un tipo anulable. La misma excepción se aplica cuando es una propiedad o método que null admite, como hashCode o toString().

Un null safety seguro (sound null safety) cambia los posibles errores en tiempo de ejecución (runtime errors) en errores de análisis en tiempo de edición (edit-time). Null safety marca una variable no nula cuando ha sido:

  • No inicializado con un valor no nulo.
  • Asignada un valor null.

Esta comprobación te permite solucionar estos errores antes de desplegar tu aplicación.

Valor por defecto (Default value)

#

Las variables no inicializadas que tienen un tipo anulable tienen un valor inicial de null. Incluso las variables con tipos numéricos son inicialmente nulas, porque los números (como todo lo demás en Dart) son objetos.

dart
int? lineCount;
assert(lineCount == null);

Con null safety, debes inicializar los valores de las variables no anulables antes de usarlas:

dart
int lineCount = 0;

No tienes que inicializar una variable local donde se declara, pero sí necesitas asignarle un valor antes de que se use. Por ejemplo, el siguiente código es válido porque Dart puede detectar que lineCount no es nulo en el momento en que se pasa a print():

dart
int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;
}

print(lineCount);

Las variables de nivel superior y de clase se inicializan de forma perezosa (lazily); el código de inicialización se ejecuta la primera vez que se usa la variable.

Variables late

#

El modificador late tiene dos casos de uso:

  • Declarar una variable no anulable que se inicializa después de su declaración.
  • Inicializar una variable de forma perezosa (lazily).

A menudo, el análisis de flujo de control de Dart puede detectar cuándo una variable no anulable se establece en un valor no nulo antes de usarse, pero a veces el análisis falla. Dos casos comunes son las variables de nivel superior y las variables de instancia: Dart a menudo no puede determinar si están establecidas, por lo que no lo intenta.

Si estás seguro de que una variable se establece antes de usarla, pero Dart no está de acuerdo, puedes solucionar el error marcando la variable como late:

dart
late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

Cuando marcas una variable como late pero la inicializas en su declaración, entonces el inicializador se ejecuta la primera vez que se usa la variable. Esta inicialización perezosa (lazy) es útil en un par de casos:

  • Es posible que la variable no sea necesaria, y su inicialización es costosa.
  • Estás inicializando una variable de instancia, y su inicializador necesita acceso a this.

En el siguiente ejemplo, si la variable temperature nunca se usa, entonces la costosa función readThermometer() nunca se llama:

dart
// This is the program's only call to readThermometer().
late String temperature = readThermometer(); // Lazily initialized.

Final y const

#

Si nunca tienes la intención de cambiar una variable, usa final o const, ya sea en lugar de var o además de un tipo. Una variable final se puede establecer solo una vez; una variable const es una constante en tiempo de compilación. (Las variables const son implícitamente final).

Aquí tienes un ejemplo de creación y establecimiento de una variable final:

dart
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

No puedes cambiar el valor de una variable final:

análisis estático: fallo dart
name = 'Alice'; // Error: a final variable can only be set once.

Usa const para variables que quieras que sean constantes en tiempo de compilación. Si la variable const está a nivel de clase, márcala como static const. Donde declares la variable, establece el valor a una constante en tiempo de compilación, como un número o literal de cadena, una variable const o el resultado de una operación aritmética con números constantes:

dart
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere

La palabra clave const no es solo para declarar variables constantes. También puedes usarla para crear valores constantes, así como para declarar constructores que crean valores constantes. Cualquier variable puede tener un valor constante.

dart
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

Puedes omitir const de la expresión de inicialización de una declaración const, como para baz arriba. Para más detalles, consulta NO uses const de forma redundante.

Puedes cambiar la referencia de una variable que no sea final ni const, incluso si antes tenía un valor const:

dart
foo = [1, 2, 3]; // Was const []

No puedes cambiar el valor de una variable const:

análisis estático: fallo dart
baz = [42]; // Error: Constant variables can't be assigned a value.

Puedes definir constantes que utilicen comprobaciones y conversiones de tipo (is y as), colecciones if (collection if), y operadores de propagación (spread) (... y ...?):

dart
const Object i = 3; // Where i is a const Object with an int value...
const list = [i as int]; // Use a typecast.
const map = {if (i is int) i: 'int'}; // Use is and collection if.
const set = {if (list is List<int>) ...list}; // ...and a spread.

Para obtener más información sobre el uso de const para crear valores constantes, consulta Listas (Lists), Mapas (Maps), y Clases (Classes).

Variables comodín

#

Una variable comodín (wildcard variable) con el nombre _ declara una variable local o parámetro que no es vinculante; esencialmente, un marcador de posición. El inicializador, si lo hay, se sigue ejecutando, pero el valor no es accesible. Pueden existir múltiples declaraciones llamadas _ en el mismo espacio de nombres sin un error de colisión.

Las declaraciones de nivel superior o los miembros donde la privacidad de la biblioteca podría verse afectada no son usos válidos para las variables comodín. Las declaraciones locales a un ámbito de bloque, como los siguientes ejemplos, pueden declarar un comodín:

  • Declaración de variables locales.

    dart
    main() {
      var _ = 1;
      int _ = 2;
    }
    
  • Declaración de variables en bucle for.

    dart
    for (var _ in list) {}
    
  • Parámetros de la cláusula catch.

    dart
    try {
      throw '!';
    } catch (_) {
      print('oops');
    }
    
  • Parámetros de tipo genérico y tipo de función.

    dart
    class T<_> {}
    void genericFunction<_>() {}
    
    takeGenericCallback(<_>() => true);
    
  • Parámetros de función.

    dart
    Foo(_, this._, super._, void _()) {}
    
    list.where((_) => true);
    
    void f(void g(int _, bool _)) {}
    
    typedef T = void Function(String _, String _);