¿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:
- Constructor generativo (Generative)
- Constructor predeterminado (Default)
- Constructor con nombre (Named)
- Constructor constante (Const)
- Constructor redirigido (Redirecting)
- Constructor de fábrica (Factory)
- Constructor de fábrica redirigido
- Sintaxis de constructor concisa (Dart 3.13+)
- 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:
- Todas las variables miembro deben llevar el modificador
final - Agrega la palabra clave
constal constructor; si usasconstal 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:
- No siempre crea objetos nuevos (reutilización de caché, devolución de instancias de subclase)
- 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 tradicional | Abreviatura |
|---|---|
Point(this.x,this.y) | new(this.x,this.y) |
Point.origin():x=0,y=0 | new 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
- Las subclases no heredan ningún constructor de la clase padre;
- Orden de ejecución del constructor: lista de inicialización → constructor de la clase padre → cuerpo del constructor de la subclase;
- 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)