Menu
Coddy logo textTech

Ограничения обобщений

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 67 из 110.

Иногда вам нужно, чтобы универсальный тип обладал определенными возможностями. Например, если вы хотите сравнить элементы или получить доступ к определенным свойствам, неограниченный T не гарантирует наличие этих возможностей. Ограничения универсальных типов позволяют ограничить параметры типа конкретными типами или их подклассами.

Используйте ключевое слово extends для ограничения параметра типа:

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');  // Ошибка!
}

Поскольку T extends num, компилятор знает, что value поддерживает числовые операции, такие как умножение. Без этого ограничения вызов value * 2 приведет к ошибке.

Ограничения также работают с вашими собственными иерархиями классов:

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);  // Безопасно: T гарантирует наличие 'name'
    }
  }
}

Ограничения обобщений сочетают в себе гибкость обобщений с безопасностью понимания того, какие операции доступны для вашего типа.

challenge icon

Задание

Легко

Давайте создадим калькулятор статистики, который работает только с числовыми типами! Вы создадите универсальный класс с ограничением, которое гарантирует, что его можно использовать только с числами, что позволит вам безопасно выполнять математические операции над сохраненными значениями.

Вы организуете свой код в два файла:

  • stats.dart: Создайте универсальный класс Stats<T extends num>, который управляет списком числовых значений:
    • List<T> с именем values для хранения чисел
    • Конструктор, который инициализирует список предоставленными значениями
    • Метод sum(), который возвращает сумму всех значений как num
    • Метод min(), который возвращает наименьшее значение как T
    • Метод max(), который возвращает наибольшее значение как T
    • Метод printStats(), который отображает статистику в отформатированном виде
  • main.dart: Импортируйте ваш файл со статистикой и продемонстрируйте работу универсального класса с ограничениями на различных числовых типах:
    • Создайте Stats<int> со значениями [10, 25, 5, 30, 15] и вызовите printStats()
    • Выведите пустую строку
    • Создайте Stats<double> со значениями [3.5, 1.2, 4.8, 2.1] и вызовите printStats()

Метод printStats() должен выводить три строки, показывающие сумму, минимальное и максимальное значения. Поскольку T extends num, вы можете безопасно использовать операторы сравнения и арифметические операции над значениями — то, что было бы невозможно с неограниченным универсальным типом.

Ожидаемый вывод:

Sum: 85
Min: 5
Max: 30

Sum: 11.6
Min: 1.2
Max: 4.8

Шпаргалка

Ограничения универсальных типов ограничивают параметры типа конкретными типами или их подклассами с помощью ключевого слова extends:

class NumberBox<T extends num> {
  T value;
  
  NumberBox(this.value);
  
  T doubled() => (value * 2) as T;
}

Благодаря T extends num компилятор знает, что value поддерживает числовые операции, такие как умножение.

Ограничения также работают с пользовательскими иерархиями классов:

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
    }
  }
}

Ограничения универсальных типов обеспечивают гибкость, гарантируя при этом типобезопасность и доступность операций.

Попробуйте сами

import 'stats.dart';

void main() {
  // TODO: Создайте Stats<int> со значениями [10, 25, 5, 30, 15]
  // и вызовите printStats()
  
  // TODO: Выведите пустую строку
  
  // TODO: Создайте Stats<double> со значениями [3.5, 1.2, 4.8, 2.1]
  // и вызовите printStats()
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование