Iterable e Iterator
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 68 de 110.
Cada vez que usas un bucle for-in en una List, Set u otra colección, estás trabajando con Iterable e Iterator internamente. Comprender estos conceptos te permite crear tus propias clases sobre las que se puede iterar.
Un Iterable es cualquier objeto que pueda producir una secuencia de valores. Tiene un requisito clave: un getter iterator que devuelve un Iterator. El Iterator es lo que realmente se desplaza a través de los elementos uno por uno:
var numbers = [1, 2, 3];
var iterator = numbers.iterator;
while (iterator.moveNext()) {
print(iterator.current); // 1, luego 2, luego 3
}El método moveNext() avanza al siguiente elemento y devuelve true si hay uno, o false cuando termina. La propiedad current le proporciona el elemento actual.
Para que tu propia clase sea iterable, implementa la interfaz 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
}
}Ahora Countdown funciona con bucles for-in y todos los métodos de Iterable como map, where y fold. Este patrón es esencial al crear colecciones personalizadas que se integran perfectamente con el ecosistema de colecciones de Dart.
Desafío
Fácil¡Vamos a construir un generador de rangos personalizado que se pueda iterar usando un bucle for-in! Crearás tus propias clases Iterable e Iterator para generar una secuencia de números dentro de un rango especificado.
Organizarás tu código en dos archivos:
range.dart: Crea dos clases que trabajen juntas para producir una secuencia de números:- Una clase
RangeIteratorque implementeIterator<int>. Debe rastrear la posición actual y saber cuándo detenerse. El iterador comienza antes del primer elemento, por lo que se debe llamar amoveNext()antes de acceder acurrent. - Una clase
Rangeque extiendaIterable<int>. Toma un valorstartyenden su constructor y proporciona un getteriteratorque devuelve unRangeIterator.
startcomoend. Por ejemplo,Range(1, 5)debería producir: 1, 2, 3, 4, 5.- Una clase
main.dart: Importa tu archivo de rango y demuestra tu iterable personalizado:- Crea un
Range(1, 5)y usa un buclefor-inpara imprimir cada número en su propia línea - Imprime una línea vacía
- Crea un
Range(10, 13)y usa el métodomappara duplicar cada valor, luego imprime la lista resultante
- Crea un
Debido a que tu clase Range extiende Iterable, automáticamente obtiene acceso a todos los potentes métodos de colección como map, where y fold; ¡esa es la belleza de implementar la interfaz Iterable!
Salida esperada:
1
2
3
4
5
[20, 22, 24, 26]Hoja de referencia
Un Iterable es cualquier objeto que puede producir una secuencia de valores. Requiere un getter iterator que devuelva un Iterator.
Un Iterator se desplaza a través de los elementos uno por uno utilizando:
moveNext(): avanza al siguiente elemento y devuelvetruesi hay uno, ofalsecuando ha terminado.current: propiedad que proporciona el elemento actual.
Uso manual de un iterador:
var numbers = [1, 2, 3];
var iterator = numbers.iterator;
while (iterator.moveNext()) {
print(iterator.current); // 1, then 2, then 3
}Para hacer que una clase personalizada sea iterable, implemente la interfaz Iterable y cree una clase Iterator correspondiente:
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
}
}Las clases que extienden Iterable funcionan con bucles for-in y obtienen automáticamente acceso a todos los métodos de Iterable como map, where y fold.
Pruébalo tú mismo
import 'range.dart';
void main() {
// TODO: Crear un Range(1, 5) y usar un bucle for-in para imprimir cada número
// TODO: Imprimir una línea vacía
// TODO: Crear un Range(10, 13), usar map para duplicar cada valor y imprimir la lista
}
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