Los Generics en Dart permiten crear clases, métodos y funciones que trabajan con tipos de datos flexibles, manteniendo la seguridad de tipos. Los Generics ayudan a evitar la duplicación de código y a aumentar su reutilización, facilitando la creación de estructuras de datos y funciones que pueden trabajar con diferentes tipos sin perder el tipo fuerte del lenguaje.
Los Generics permiten definir un "tipo placeholder" en una clase, función o método que luego puede ser reemplazado por cualquier tipo específico al instanciar la clase o llamar al método. Se representan mediante <T>
, donde T
es el tipo genérico. Por convención, T
, K
, V
, E
, o cualquier letra mayúscula se usan como nombres para estos parámetros de tipo.
Para definir una clase o función genérica, se usa la notación <T>
después del nombre de la clase o de la función. Por ejemplo:
class Caja<T> {
T contenido;
Caja(this.contenido);
T obtenerContenido() => contenido;
}
En este ejemplo, Caja
es una clase genérica que puede contener cualquier tipo de dato, definido al momento de la instancia.
A continuación, se muestra una clase Caja
que almacena cualquier tipo de valor:
class Caja<T> {
T contenido;
Caja(this.contenido);
T obtenerContenido() => contenido;
}
void main() {
var cajaDeEnteros = Caja<int>(5);
print("Contenido de cajaDeEnteros: ${cajaDeEnteros.obtenerContenido()}"); // Imprime: 5
var cajaDeStrings = Caja<String>("Hola");
print("Contenido de cajaDeStrings: ${cajaDeStrings.obtenerContenido()}"); // Imprime: Hola
}
También puedes definir funciones que acepten o devuelvan valores genéricos:
T retornarPrimero<T>(T a, T b) {
return a;
}
void main() {
print(retornarPrimero<int>(3, 5)); // Imprime: 3
print(retornarPrimero<String>("Hola", "Mundo")); // Imprime: Hola
}
Puedes definir múltiples tipos genéricos en una clase o función utilizando <T, U, V, ...>
:
class Par<T, U> {
T primero;
U segundo;
Par(this.primero, this.segundo);
}
void main() {
var par = Par<int, String>(1, "uno");
print("Par: ${par.primero} y ${par.segundo}"); // Imprime: Par: 1 y uno
}
En este caso, la clase Par
acepta dos tipos distintos (T
y U
) para representar dos valores diferentes.
Es posible restringir los tipos que pueden usarse en un Generic mediante extends
. Esto asegura que solo tipos específicos puedan utilizarse como argumentos genéricos.
class CajaNumerica<T extends num> {
T contenido;
CajaNumerica(this.contenido);
T doble() => contenido * 2 as T;
}
void main() {
var cajaInt = CajaNumerica<int>(5);
print("Doble: ${cajaInt.doble()}"); // Imprime: Doble: 10
var cajaDouble = CajaNumerica<double>(2.5);
print("Doble: ${cajaDouble.doble()}"); // Imprime: Doble: 5.0
}
Aquí, CajaNumerica
solo acepta tipos numéricos (int
y double
), porque el tipo T
está restringido con extends num
.
Para obtener más información, puedes consultar la documentación oficial de Dart sobre Generics.
Jorge García
Fullstack developer