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