Menu
Coddy logo textTech

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.

challenge icon

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érica Stats<T extends num> que gestione una lista de valores numéricos:
    • Una List<T> llamada values para 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 un num
    • Un método min() que devuelva el valor más pequeño como T
    • Un método max() que devuelva el valor más grande como T
    • Un método printStats() que muestre las estadísticas de forma formateada
  • 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 a printStats()
    • Imprime una línea vacía
    • Crea un Stats<double> con los valores [3.5, 1.2, 4.8, 2.1] y llama a printStats()

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.8

Hoja 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()
}
quiz iconPonte a prueba

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