Ограничения обобщений
Часть раздела Объектно-ориентированное программирование путешествия по 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'
}
}
}Ограничения обобщений сочетают в себе гибкость обобщений с безопасностью понимания того, какие операции доступны для вашего типа.
Задание
ЛегкоДавайте создадим калькулятор статистики, который работает только с числовыми типами! Вы создадите универсальный класс с ограничением, которое гарантирует, что его можно использовать только с числами, что позволит вам безопасно выполнять математические операции над сохраненными значениями.
Вы организуете свой код в два файла:
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()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий