Restricciones genéricas
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 67 de 110.
A veces necesitas que un tipo genérico tenga ciertas capacidades. Por ejemplo, si quieres comparar elementos o acceder a propiedades específicas, un T sin restricciones no garantizará que esas características existan. Las restricciones genéricas te permiten restringir los parámetros de tipo a tipos específicos o sus subclases.
Utiliza la palabra clave extends para restringir un parámetro de tipo:
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
T doubled() => (value * 2) as T;
}
void main() {
var intBox = NumberBox<int>(5);
var doubleBox = NumberBox<double>(3.5);
print(intBox.doubled()); // 10
print(doubleBox.doubled()); // 7.0
// var stringBox = NumberBox<String>('hi'); // ¡Error!
}Debido a que T extends num, el compilador sabe que value admite operaciones numéricas como la multiplicación. Sin esta restricción, llamar a value * 2 fallaría.
Las restricciones también funcionan con tus propias jerarquías de clases:
abstract class Animal {
String get name;
}
class Dog extends Animal {
String get name => 'Dog';
}
class Shelter<T extends Animal> {
List<T> animals = [];
void add(T animal) => animals.add(animal);
void printNames() {
for (var animal in animals) {
print(animal.name); // Seguro: T garantiza que 'name' existe
}
}
}Las restricciones genéricas combinan la flexibilidad de los genéricos con la seguridad de saber qué operaciones están disponibles en su tipo.
Desafío
Fácil¡Vamos a construir una calculadora de estadísticas que solo funcione con tipos numéricos! Crearás una clase genérica con una restricción que asegura que solo se pueda usar con números, lo que te permitirá realizar operaciones matemáticas de forma segura sobre los valores almacenados.
Organizarás tu código en dos archivos:
stats.dart: Crea una clase genéricaStats<T extends num>que gestione una lista de valores numéricos:- Una
List<T>llamadavaluespara almacenar los números - Un constructor que inicialice la lista con los valores proporcionados
- Un método
sum()que devuelva la suma de todos los valores como unnum - Un método
min()que devuelva el valor más pequeño comoT - Un método
max()que devuelva el valor más grande comoT - Un método
printStats()que muestre las estadísticas de forma formateada
- Una
main.dart: Importa tu archivo de estadísticas y demuestra la clase genérica restringida con diferentes tipos numéricos:- Crea un
Stats<int>con los valores[10, 25, 5, 30, 15]y llama aprintStats() - Imprime una línea vacía
- Crea un
Stats<double>con los valores[3.5, 1.2, 4.8, 2.1]y llama aprintStats()
- Crea un
El método printStats() debe mostrar tres líneas con la suma, el valor mínimo y el valor máximo. Debido a que T extends num, puedes usar con seguridad operadores de comparación y operaciones aritméticas en los valores, algo que no sería posible con un tipo genérico sin restricciones.
Salida esperada:
Sum: 85
Min: 5
Max: 30
Sum: 11.6
Min: 1.2
Max: 4.8Hoja de referencia
Las restricciones genéricas limitan los parámetros de tipo a tipos específicos o sus subclases utilizando la palabra clave extends:
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
T doubled() => (value * 2) as T;
}Con T extends num, el compilador sabe que value admite operaciones numéricas como la multiplicación.
Las restricciones también funcionan con jerarquías de clases personalizadas:
abstract class Animal {
String get name;
}
class Shelter<T extends Animal> {
List<T> animals = [];
void printNames() {
for (var animal in animals) {
print(animal.name); // Safe: T guarantees 'name' exists
}
}
}Las restricciones genéricas proporcionan flexibilidad al tiempo que garantizan la seguridad de tipos y las operaciones disponibles.
Pruébalo tú mismo
import 'stats.dart';
void main() {
// TODO: Crea un Stats<int> con los valores [10, 25, 5, 30, 15]
// y llama a printStats()
// TODO: Imprime una línea vacía
// TODO: Crea un Stats<double> con los valores [3.5, 1.2, 4.8, 2.1]
// y llama a printStats()
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository