Genéricos
Aprende sobre los tipos genéricos en Dart.
Si observas la documentación de la API para el tipo de array básico,
List, verás que el
tipo es en realidad List<E>. La notación <...> marca a List como un tipo
genérico (o parametrizado), un tipo que tiene parámetros de tipo
formales. Por convención, la mayoría de las variables de tipo tienen nombres de una sola letra,
como E, T, S, K y V.
¿Por qué usar genéricos?
#Los genéricos a menudo son necesarios para la seguridad de tipos, pero tienen más beneficios además de permitir que tu código se ejecute:
- Especificar adecuadamente los tipos genéricos da como resultado un mejor código generado.
- Puedes usar genéricos para reducir la duplicación de código.
Si pretendes que una lista contenga solo cadenas, puedes
declararla como List<String> (léelo como "lista de cadenas"). De esa manera,
tú, tus compañeros programadores y tus herramientas pueden detectar que asignar algo que no sea una cadena a
la lista es probablemente un error. Aquí tienes un ejemplo:
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
Otra razón para usar genéricos es reducir la duplicación de código. Los genéricos te permiten compartir una única interfaz e implementación entre muchos tipos, mientras sigues aprovechando el análisis estático. Por ejemplo, supongamos que creas una interfaz para almacenar en caché un objeto:
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
Descubres que quieres una versión específica para cadenas de esta interfaz, por lo que creas otra interfaz:
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
Más tarde, decides que quieres una versión específica para números de esta interfaz... Ya te haces una idea.
Los tipos genéricos pueden ahorrarte la molestia de crear todas estas interfaces. En su lugar, puedes crear una única interfaz que tome un parámetro de tipo:
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
En este código, T es el tipo sustituto. Es un marcador de posición (placeholder) que puedes considerar como un tipo que un desarrollador definirá más tarde.
Usar literales de colección
#Los literales de listas, sets y mapas pueden ser parametrizados. Los literales parametrizados son
exactamente iguales a los literales que ya has visto, excepto que añades
<tipo> (para listas y sets) o
<tipoClave, tipoValor> (para mapas)
antes de la llave de apertura. Aquí tienes un ejemplo de uso de literales con tipo:
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines',
};
Usar tipos parametrizados con constructores
#Para especificar uno o más tipos al usar un constructor, coloca los tipos entre
corchetes angulares (<...>) justo después del nombre de la clase. Por ejemplo:
var nameSet = Set<String>.of(names);
El siguiente código crea un SplayTreeMap que tiene
claves enteras y valores de tipo View:
var views = SplayTreeMap<int, View>();
Colecciones genéricas y los tipos que contienen
#Los tipos genéricos de Dart son reificados (reified), lo que significa que conservan su información de tipo en tiempo de ejecución. Por ejemplo, puedes probar el tipo de una colección:
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
Restringir el tipo parametrizado
#Al implementar un tipo genérico,
es posible que desees limitar los tipos que se pueden proporcionar como argumentos,
de modo que el argumento deba ser un subtipo de un tipo en particular.
Esta restricción se denomina límite (bound).
Puedes hacer esto usando extends.
Un caso de uso común es garantizar que un tipo no sea anulable
al convertirlo en un subtipo de Object
(en lugar del valor predeterminado, Object?).
class Foo<T extends Object> {
// Any type provided to Foo for T must be non-nullable.
}
Puedes usar extends con otros tipos además de Object.
Aquí tienes un ejemplo de extensión de SomeBaseClass,
de modo que los miembros de SomeBaseClass se puedan llamar en objetos de tipo T:
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {
...
}
Está bien usar SomeBaseClass o cualquiera de sus subtipos como argumento genérico:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
También está bien no especificar ningún argumento genérico:
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
Especificar cualquier tipo que no sea SomeBaseClass da como resultado un error:
var foo = Foo<Object>();
Restricciones de parámetros de tipo autorreferenciales (F-bounds)
#Al usar límites para restringir los tipos de parámetros, puedes hacer referencia del límite al propio parámetro de tipo. Esto crea una restricción autorreferencial, o F-bound. Por ejemplo:
abstract interface class Comparable<T> {
int compareTo(T o);
}
int compareAndOffset<T extends Comparable<T>>(T t1, T t2) =>
t1.compareTo(t2) + 1;
class A implements Comparable<A> {
@override
int compareTo(A other) => /*...implementation...*/ 0;
}
int useIt = compareAndOffset(A(), A());
El F-bound T extends Comparable<T> significa que T debe ser comparable consigo mismo.
Por lo tanto, A solo se puede comparar con otras instancias del mismo tipo.
Usar métodos genéricos
#Los métodos y funciones también permiten argumentos de tipo:
T first<T>(List<T> ts) {
// Do some initial work or error checking, then...
T tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
Aquí el parámetro de tipo genérico en first (<T>)
te permite usar el argumento de tipo T en varios lugares:
- En el tipo de retorno de la función (
T). - En el tipo de un argumento (
List<T>). - En el tipo de una variable local (
T tmp).
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2025-07-03. Ver fuente oreportar un problema.