¿Existe alguna diferencia en cómo se inicializan las variables miembro en Dart?
En Dart, ¿hay alguna diferencia entre la asignación de valores de inmediato y el constructor como en Java?
class Example {
int x = 3;
}
vs
class Example {
int x;
Example() {
x = 3;
}
}
Pregunto porque cuando estaba usando Flutter e intenté asignar una Función que usa setState a una variable, no fue posible con el método anterior pero sí con el segundo.
En tu caso trivial, no importa.
En general, puedes inicializar variables de instancia de varias maneras:
En línea (inicializadores de campo)
class Example1 {
T x = value;
}
Ventajas:
- Directo, conciso.
- El miembro se inicializará en todos los constructores.
- Se puede utilizar para inicializar
final
o miembros que no aceptan valores NULL. - El miembro se inicializa antes de invocar a los constructores de la clase base, lo cual es importante cuando el constructor de la clase base llama a funciones miembro que la clase derivada anula.
Desventajas:
- No puede depender de argumentos de construcción.
- Por lo general, no puede depender de él
this
ya que la inicialización ocurre antes dethis
que sea válida (es decir, no puede depender de otros miembros de la instancia). ( Una excepción es si el miembro se inicializa de forma diferida al declararlolate
. Esto requiere que esté habilitada la característica de seguridad nula).
Lista de inicializadores
class Example2 {
T x;
Example2() : x = value;
}
Ventajas:
- Se puede utilizar para inicializar
final
o miembros que no aceptan valores NULL. - El miembro se inicializa antes de invocar a los constructores de la clase base, lo cual es importante cuando el constructor de la clase base llama a funciones miembro que la clase derivada anula.
- Puede utilizar argumentos de construcción.
- La variable inicializada siempre hace referencia a una variable miembro, nunca a un parámetro del constructor.
Desventajas:
- Si la clase tiene varios constructores, la inicialización debería duplicarse o los constructores deberían redirigir a un constructor común.
- No se puede depender de él
this
ya que la inicialización ocurre antes dethis
que sea válida (es decir, no se puede depender de otros miembros de la instancia). - Sólo puede inicializar miembros de la clase adjunta. Debido a que las listas de inicializadores se ejecutan antes de invocar a los constructores de la clase base, no pueden establecer miembros de la clase base.
Cuerpo constructor
class Example3 {
T x;
Example3() {
x = value;
}
}
Ventajas:
- Puede utilizar argumentos de construcción.
- Se puede utilizar para realizar una inicialización más complicada, como en los casos en los que el miembro no se puede inicializar mediante una única expresión.
- Puede usar
this
(es decir, puede usar otros miembros de la instancia). - Se puede utilizar para configurar miembros de la clase base.
Desventajas:
- No se puede utilizar para inicializar
late
final
miembros que no admiten valores NULL. - Si la clase tiene varios constructores, la inicialización deberá duplicarse o el código de inicialización deberá refactorizarse (como, entre otros, redirigir a un constructor común).
- El miembro se inicializa después de invocar los constructores de la clase base.
- Si el constructor tiene un parámetro que oculta una variable miembro, es fácil hacer referencia accidentalmente al parámetro en lugar del miembro. (Consulte https://github.com/dart-lang/linter/issues/2552 para obtener más detalles).
Probablemente hay algunos puntos que estoy olvidando, pero creo que esto debería cubrir los principales.
Primero se produce la inicialización directa en línea, luego las listas de inicialización y luego los cuerpos del constructor. Consulte también Diferencia entre asignar valores en la lista de parámetros y la lista de inicializadores , que explica por qué this
se vuelve válido solo para las etapas posteriores de la inicialización del objeto.
Como ejemplo donde importa dónde se inicializan los miembros:
class Base {
Base() {
doSomething();
}
void doSomething() {}
}
class DerivedEarly extends Base {
int? x;
DerivedEarly() : x = 42;
@override
void doSomething() => print(x);
}
class DerivedLate extends Base {
int? x;
DerivedLate() {
x = 42;
}
@override
void doSomething() => print(x);
}
void main() {
DerivedEarly(); // Prints: 42
DerivedLate(); // Prints: null
}