Constructores y listas de inicialización en Dart

Un constructor es el mecanismo encargado de crear una nueva instancia de una clase y establecer su estado inicial. Si no defines ninguno, Dart te proporciona un constructor por defecto (vacío y sin parámetros) para que puedas instanciar la clase. Sin embargo, en aplicaciones reales, casi siempre necesitas pasarle datos al objeto en el momento de su creación.

Para lograr esto, usamos parámetros en el constructor. Dart ofrece una sintaxis extremadamente limpia llamada parámetros de inicialización formal (como this.campo), que es un atajo para asignar el valor recibido directamente al atributo de la clase, eliminando la necesidad de escribir código extra en el cuerpo del constructor. Para casos donde la asignación simple no basta, entra en juego la lista de inicialización. Esta es una sección de código que se ejecuta después de los parámetros pero antes de que el cuerpo del constructor se ejecute, separada por dos puntos (:).

Es fundamental usar la lista de inicialización cuando necesites calcular el valor de un campo final antes de que el objeto se termine de construir, cuando quieras ejecutar validaciones mediante assert para asegurar que los datos son correctos, o cuando necesites llamar al constructor de la clase padre (super). Si intentas realizar estas tareas dentro del cuerpo del constructor (dentro de las llaves {}), el programa fallará si los campos son final, ya que para ese momento el objeto ya se considera “construido” y sus campos inmutables no pueden ser modificados.

class Vehiculo {
  final String marca;
  final String modelo;

  // Usamos super.marca y super.modelo para pasar los valores directamente
  // al constructor de la clase padre [disponible desde Dart 2.17]
  Vehiculo(super.marca, super.modelo);
}

class Coche extends Vehiculo {
  final int cilindrada;
  final String tipoCombustible;

  // 1. 'super.marca' y 'super.modelo' pasan argumentos al padre.
  // 2. 'this.cilindrada' asigna el parámetro directamente al campo.
  // 3. La lista de inicialización (después de ':') hace el trabajo pesado.
  Coche(
    super.marca,
    super.modelo,
    this.cilindrada,
  ) : assert(cilindrada > 0, 'La cilindrada debe ser mayor a 0'),
       tipoCombustible = cilindrada > 1500 ? 'Gasolina' : 'Mixto';

  void mostrarDetalles() {
    print('Coche $marca $modelo: $cilindrada cc ($tipoCombustible)');
  }
}

void main() {
  // Instanciamos un coche con 1600 cc. 
  // El cálculo de 'tipoCombustible' ocurre en la lista de inicialización.
  final miCoche = Coche('Toyota', 'Corolla', 1600);
  miCoche.mostrarDetalles();

  // Este generaría un error de tiempo de ejecución por el 'assert'
  // final cocheError = Coche('Fiat', '500', -500);
}

En el ejemplo anterior, fíjate cómo Coche hereda de Vehiculo. Gracias a los super initializer parameters (super.marca), no tenemos que escribir una lógica manual para pasar esos valores al padre; Dart lo hace por nosotros. El campo cilindrada se asigna directamente mediante this.cilindrada.

Lo más importante ocurre después de los dos puntos (:). La expresión assert(cilindrada > 0) actúa como un guardián: si intentas crear un coche con cilindrada negativa, el programa se detendrá inmediatamente antes de que el objeto se cree. Al mismo tiempo, tipoCombustible se inicializa mediante una expresión condicional. Como tipoCombustible es final, la única forma de asignarle un valor basado en otra variable (cilindrada) es precisamente en esta lista de inicialización, ya que el cuerpo del constructor { ... } llegaría demasiado tarde para modificar un campo final. Notarás que el constructor de Coche termina en ; y no tiene llaves {}; esto es porque toda la lógica de inicialización ya se completó en la lista de inicialización.

El error frecuente

Si intentas inicializar un campo final dentro del cuerpo del constructor, te encontrarás con un error de compilación.

class ErrorEjemplo {
  final int valor;

  // Esto fallará al compilar
  ErrorEjemplo(int v) {
    // ERROR: Un campo 'final' debe asignarse antes de que el cuerpo se ejecute.
    // valor = v; 
  }
}

Para corregirlo, debes usar la lista de inicialización o la sintaxis de inicialización formal:

// Opción correcta 1: Initializing formal
ErrorEjemplo(this.valor);

// Opción correcta 2: Lista de inicialización
ErrorEjemplo(int v) : valor = v;

49

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio