constructor

¿Qué es un constructor?

El constructor es una función especial que sirve para crear instancias de clase, se usa normalmente para inicializar propiedades. Excepto el constructor predeterminado, todos los constructores comparten el mismo nombre que la clase.

Clasificación de constructores en Dart:

  1. Constructor generativo (Generative)
  2. Constructor predeterminado (Default)
  3. Constructor con nombre (Named)
  4. Constructor constante (Const)
  5. Constructor redirigido (Redirecting)
  6. Constructor de fábrica (Factory)
  7. Constructor de fábrica redirigido
  8. Sintaxis de constructor concisa (Dart 3.13+)
  9. Constructor primario (Primary Constructors, sintaxis compacta de campos + constructor)

1. Constructor generativo Generative Constructor

El constructor más básico, crea instancias e inicializa variables miembro. Admite parámetros inicializadores this.variable para simplificar las asignaciones.

class Point {
  double x;
  double y;

  // Constructor generativo, this.x recibe directamente el parámetro para asignar al miembro
  Point(this.x, this.y); // Asigna valores directamente a x e y
}

void main() {
  final p = Point(10, 20);
  print("(${p.x}, ${p.y})"); // (10, 20)
}
Lenguaje del código: JavaScript (javascript)

2. Constructor predeterminado Default Constructor

Si no defines manualmente ningún constructor en la clase, Dart proporciona automáticamente un constructor generativo sin parámetros y sin nombre. Este comportamiento es igual al de otros lenguajes tipo C como C++, Java, C#, etc.

class PointA {
  double x = 0;
  double y = 0;
  // Constructor predeterminado implícito: PointA();
}

void main() {
  final p = PointA(); // Invoca automáticamente el constructor predeterminado
  print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Lenguaje del código: PHP (php)

Aviso: En cuanto escribas cualquier constructor manualmente, el constructor predeterminado desaparece.

3. Constructor con nombre Named Constructor

Propósito: Definir varios constructores dentro de una misma clase con semántica clara; formato NombreClase.NombreConstructor()

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // Constructor generativo principal
  Point(this.x, this.y);

  // Constructor con nombre: punto de origen
  Point.origin() : x = xOrigin, y = yOrigin;
}

void main() {
  final p1 = Point(5, 6);
  final p2 = Point.origin(); // Invocar constructor con nombre
  print(p1.x); //5
  print("Punto de origen: (${p2.x}, ${p2.y})"); // (0,0)
}
Lenguaje del código: PHP (php)

Reglas importantes

  • Las subclases no heredan los constructores con nombre de la clase padre; si necesitas un constructor con nombre igual en la subclase, debes implementarlo manualmente;
  • Los constructores con nombre admiten listas de inicialización para asignar valores a variables final.

4. Constructor constante Const Constructor

Se usa para crear objetos constantes en tiempo de compilación, requisitos:

  1. Todas las variables miembro deben llevar el modificador final
  2. Agrega la palabra clave const al constructor; si usas const al instanciar, se reutiliza la caché de constantes. Si omites const, se crea una instancia ordinaria nueva.
class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0); // Estático perteneciente a la clase, compartido por toda la clase
  final double x, y;

  // El constructor constante puede inicializar x e y declarados como final
  const ImmutablePoint(this.x, this.y);
}

void main() {
  // Constante de compilación, reutiliza el mismo objeto
  const p1 = ImmutablePoint(1, 2);
  const p2 = ImmutablePoint(1, 2); // Solo si los parámetros son idénticos se comparte la instancia
  print(p1 == p2); // true

  // Instancia ordinaria, objeto nuevo independiente
  final p3 = ImmutablePoint(1, 2);
  print(p1 == p3); // false
}Lenguaje del código: PHP (php)

5. Constructor redirigido Redirecting Constructor

Constructor que reenvía la ejecución a otro constructor de la misma clase mediante la sintaxis : this(parámetros), el cuerpo de la función debe estar vacío.

class Point {
  double x, y;

  // Constructor principal
  Point(this.x, this.y);

  // Constructor redirigido: solo recibe x, y fijado en 0, reenvía al constructor principal
  Point.alongX(double x) : this(x, 0); // Invoca el constructor con nombre y luego pasa x al constructor principal fijando y=0
}

void main() {
  final p = Point.alongX(9);
  print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Lenguaje del código: JavaScript (javascript)

6. Constructor de fábrica Factory Constructor

Palabra clave factory, escenarios de uso:

  1. No siempre crea objetos nuevos (reutilización de caché, devolución de instancias de subclase)
  2. Lógica compleja antes de crear la instancia (análisis JSON, validación de parámetros). Restricción: no se puede acceder a this
Ejemplo de Logger singleton con caché
class Logger {
  final String name;
  bool mute = false;
  // Caché privado, solo visible dentro de la librería
  static final Map<String, Logger> _cache = {};

  // Constructor de fábrica: primero recupera desde la caché
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // Constructor de fábrica: crear desde el análisis JSON
  factory Logger.fromJson(Map<String, dynamic> json) {
    return Logger(json["name"].toString());
  }

  // Constructor generativo interno privado
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

void main() {
  final log1 = Logger("UI");
  final log2 = Logger("UI");
  print(log1 == log2); // true, mismo objeto de caché

  final jsonLog = Logger.fromJson({"name": "Net"});
  jsonLog.log("request success");
}
Lenguaje del código: JavaScript (javascript)

7. Constructor de fábrica redirigido Redirecting Factory

Sintaxis factory Nombre() = ConstructorOtraClase;, ideal para clases abstractas que delegan la creación de instancias y evitan repetir definiciones de parámetros.

abstract class Listenable {}

class _MergingListenable implements Listenable {
  List<_MergingListenable> list;
  _MergingListenable(this.list);
}

// Fábrica redirigida, invoca el constructor de la clase privada interna
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Lenguaje del código: PHP (php)

8. Desacoplamiento de constructores Constructor Tear-offs

Pasa directamente el constructor como argumento de función sin escribir (), reemplaza las funciones lambda anónimas redundantes y mejora el rendimiento.

Forma recomendada
void main() {
  final charCodes = [65, 66, 67];
  // Constructor con nombre String.fromCharCode
  final strs = charCodes.map(String.fromCharCode);
  print(strs); // (A, B, C)

  // Desacoplamiento de constructor sin nombre con .new
  final buffers = charCodes.map(StringBuffer.new);
}
Lenguaje del código: PHP (php)
No recomendado: lambda redundante
// Envoltorio innecesario, evitar esta forma
charCodes.map((code) => String.fromCharCode(code));
Lenguaje del código: JavaScript (javascript)

9. Sintaxis de constructor concisa (Dart 3.13+) Concise Constructor

Dentro de la clase se puede omitir el nombre de la clase, usar directamente new / factory para definir constructores sin escribir NombreClase.xxx:

Forma tradicionalAbreviatura
Point(this.x,this.y)new(this.x,this.y)
Point.origin():x=0,y=0new origin():x=0,y=0
factory Point.clone()factory clone()
class Point {
  double x, y;

  // Abreviatura de constructor generativo sin nombre
  new(this.x, this.y);

  // Abreviatura de constructor generativo con nombre
  new origin() : x = 0, y = 0;

  // Abreviatura de constructor de fábrica
  factory clone(Point other) => new(other.x, other.y);
}

void main() {
  final p = new(3, 4);
  final origin = new origin();
  final copy = p.clone(p);
}
Lenguaje del código: PHP (php)

10. Tres formas de inicializar variables de instancia

1: Asignación directa al declarar
class PointA {
  double x = 1.0;
  double y = 2.0;
}
Lenguaje del código: JavaScript (javascript)

2: Parámetros inicializadores

this.variable

Admite parámetros opcionales posicionales, parámetros con nombre y variables nullables

class PointB {
  final double x;
  final double y;

  // Parámetros opcionales posicionales
  PointB.opt([this.x = 0, this.y = 0]);

  // Parámetros con nombre con valor por defecto
  PointB.named({this.x = 5, this.y = 5});
}
Lenguaje del código: JavaScript (javascript)

3: Lista de inicialización Initializer List

Asignaciones después de los dos puntos :, se ejecutan antes del cuerpo del constructor, no se puede acceder a this, admite assert para validación de parámetros.

class Point {
  final double x;
  final double y;

  Point.fromJson(Map<String, double> json)
      : x = json["x"]!,
        y = json["y"]! {
    print("Se ejecuta el cuerpo del constructor, x=$x");
  }

  // Con validación mediante assert
  Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Lenguaje del código: JavaScript (javascript)

11. Parámetros inicializadores directos para campos privados (Dart3.12+)

Para variables privadas con prefijo guion bajo, escribe los parámetros del constructor como this._campo; al invocar desde fuera usa directamente el nombre público sin guion bajo.

class Point {
  final double _x;
  final double _y;

  // Internamente usa _x, al invocar externamente se escribe x:10
  Point({required this._x, this._y = 0});
}

void main() {
  final p = Point(x: 10); // No hace falta escribir _x, directamente x
  print(p._x); // 10
}
Lenguaje del código: PHP (php)

Usa el nombre original de la variable privada dentro de la lista de inicialización:

class Point {
  final double _x;
  Point({required this._x}) : assert(_x > 0);
}
Lenguaje del código: JavaScript (javascript)

12. Constructores de la clase padre y reglas de herencia

  1. Las subclases no heredan ningún constructor de la clase padre;
  2. Orden de ejecución del constructor: lista de inicialización → constructor de la clase padre → cuerpo del constructor de la subclase;
  3. Si la clase padre no dispone de constructor predeterminado sin parámetros, la subclase debe invocar manualmente super() para indicar el constructor del padre.
Ejemplo básico de llamada a super

Al igual que en Java, se usa super para referirse a la clase base; en C# se usa base.

class Person {
  final String name;
  Person(this.name);
  Person.fromMap(Map map) : name = map["name"];
}

class Employee extends Person {
  int id;
  // Invocar constructor con nombre del padre
  Employee(Map data) : super.fromMap(data), id = data["id"];
}
Lenguaje del código: JavaScript (javascript)

Aviso: En las expresiones de parámetros de super no se puede usar this.

Parámetros Super (Dart2.17+, simplifica el envío de parámetros al padre)

Usa super.variable para reenviar parámetros directamente al constructor del padre sin escribir manualmente super(x,y)

class Vector2d {
  final double x, y;
  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;
  // super.x super.y se reenvían automáticamente al constructor padre
  Vector3d(super.x, super.y, this.z);
}

// Constructor con nombre del padre + parámetros con nombre super
class Vector3dYZ extends Vector2d {
  final double z;
  Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Lenguaje del código: JavaScript (javascript)

Deja una respuesta

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