Iterable и Iterator
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 68 из 110.
Каждый раз, когда вы используете цикл for-in для List, Set или другой коллекции, вы работаете с Iterable и Iterator «под капотом». Понимание этих концепций позволяет вам создавать собственные классы, которые можно обходить в цикле.
Iterable — это любой объект, который может генерировать последовательность значений. У него есть одно ключевое требование: геттер iterator, который возвращает Iterator. Iterator — это то, что на самом деле перебирает элементы один за другим:
var numbers = [1, 2, 3];
var iterator = numbers.iterator;
while (iterator.moveNext()) {
print(iterator.current); // 1, затем 2, затем 3
}Метод moveNext() переходит к следующему элементу и возвращает true, если он существует, или false по завершении. Свойство current возвращает текущий элемент.
Чтобы сделать ваш собственный класс итерируемым, реализуйте интерфейс Iterable:
class Countdown extends Iterable<int> {
final int start;
Countdown(this.start);
@override
Iterator<int> get iterator => CountdownIterator(start);
}
class CountdownIterator implements Iterator<int> {
int _current;
CountdownIterator(int start) : _current = start + 1;
@override
int get current => _current;
@override
bool moveNext() {
if (_current > 0) {
_current--;
return true;
}
return false;
}
}
void main() {
for (var n in Countdown(3)) {
print(n); // 3, 2, 1, 0
}
}Теперь Countdown работает с циклами for-in и всеми методами Iterable, такими как map, where и fold. Этот паттерн важен при создании пользовательских коллекций, которые бесшовно интегрируются с экосистемой коллекций Dart.
Задание
ЛегкоДавайте создадим собственный генератор диапазона, который можно перебирать с помощью цикла for-in! Вы создадите свои собственные классы Iterable и Iterator для генерации последовательности чисел в заданном диапазоне.
Вы организуете свой код в двух файлах:
range.dart: Создайте два класса, которые работают вместе для создания последовательности чисел:- Класс
RangeIterator, который реализуетIterator<int>. Он должен отслеживать текущую позицию и знать, когда остановиться. Итератор начинается перед первым элементом, поэтомуmoveNext()должен быть вызван перед обращением кcurrent. - Класс
Range, который расширяетIterable<int>. Он принимает значенияstartиendв своем конструкторе и предоставляет геттерiterator, который возвращаетRangeIterator.
start, так иend. Например,Range(1, 5)должен выдавать: 1, 2, 3, 4, 5.- Класс
main.dart: Импортируйте ваш файл range и продемонстрируйте работу вашего итерируемого объекта:- Создайте
Range(1, 5)и используйте циклfor-in, чтобы вывести каждое число на отдельной строке - Выведите пустую строку
- Создайте
Range(10, 13)и используйте методmap, чтобы удвоить каждое значение, затем выведите полученный список
- Создайте
Поскольку ваш класс Range расширяет Iterable, он автоматически получает доступ ко всем мощным методам коллекций, таким как map, where и fold — в этом и заключается прелесть реализации интерфейса Iterable!
Ожидаемый результат:
1
2
3
4
5
[20, 22, 24, 26]Шпаргалка
Iterable — это любой объект, который может создавать последовательность значений. Ему требуется геттер iterator, который возвращает Iterator.
Iterator перемещается по элементам один за другим, используя:
moveNext()— переходит к следующему элементу и возвращаетtrue, если он есть, илиfalse, когда элементы закончилисьcurrent— свойство, которое возвращает текущий элемент
Использование итератора вручную:
var numbers = [1, 2, 3];
var iterator = numbers.iterator;
while (iterator.moveNext()) {
print(iterator.current); // 1, затем 2, затем 3
}Чтобы сделать пользовательский класс итерируемым, реализуйте интерфейс Iterable и создайте соответствующий класс Iterator:
class Countdown extends Iterable<int> {
final int start;
Countdown(this.start);
@override
Iterator<int> get iterator => CountdownIterator(start);
}
class CountdownIterator implements Iterator<int> {
int _current;
CountdownIterator(int start) : _current = start + 1;
@override
int get current => _current;
@override
bool moveNext() {
if (_current > 0) {
_current--;
return true;
}
return false;
}
}
void main() {
for (var n in Countdown(3)) {
print(n); // 3, 2, 1, 0
}
}Классы, расширяющие Iterable, работают с циклами for-in и автоматически получают доступ ко всем методам Iterable, таким как map, where и fold.
Попробуйте сами
import 'range.dart';
void main() {
// TODO: Создайте Range(1, 5) и используйте цикл for-in для вывода каждого числа
// TODO: Выведите пустую строку
// TODO: Создайте Range(10, 13), используйте map для удвоения каждого значения и выведите список
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий