Menu
Coddy logo textTech

Iterable e Iterator

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 68 de 110.

Toda vez que você usa um loop for-in em uma List, Set ou outra coleção, você está trabalhando com Iterable e Iterator por baixo dos panos. Entender esses conceitos permite que você crie suas próprias classes que podem ser percorridas em um loop.

Um Iterable é qualquer objeto que pode produzir uma sequência de valores. Ele possui um requisito fundamental: um getter iterator que retorna um Iterator. O Iterator é o que realmente percorre os elementos um por um:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1, depois 2, depois 3
}

O método moveNext() avança para o próximo elemento e retorna true se houver um, ou false quando terminar. A propriedade current fornece o elemento atual.

Para tornar sua própria classe iterável, implemente a interface 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
  }
}

Agora o Countdown funciona com loops for-in e todos os métodos Iterable como map, where e fold. Este padrão é essencial ao construir coleções personalizadas que se integram perfeitamente ao ecossistema de coleções do Dart.

challenge icon

Desafio

Fácil

Vamos construir um gerador de intervalo personalizado que pode ser iterado usando um loop for-in! Você criará suas próprias classes Iterable e Iterator para gerar uma sequência de números dentro de um intervalo especificado.

Você organizará seu código em dois arquivos:

  • range.dart: Crie duas classes que trabalham juntas para produzir uma sequência de números:
    • Uma classe RangeIterator que implementa Iterator<int>. Ela deve rastrear a posição atual e saber quando parar. O iterador começa antes do primeiro elemento, então moveNext() deve ser chamado antes de acessar current.
    • Uma classe Range que estende Iterable<int>. Ela recebe um valor start e end em seu construtor e fornece um getter iterator que retorna um RangeIterator.
    O intervalo deve ser inclusivo tanto para start quanto para end. Por exemplo, Range(1, 5) deve produzir: 1, 2, 3, 4, 5.
  • main.dart: Importe seu arquivo de intervalo e demonstre seu iterável personalizado:
    • Crie um Range(1, 5) e use um loop for-in para imprimir cada número em sua própria linha
    • Imprima uma linha vazia
    • Crie um Range(10, 13) e use o método map para dobrar cada valor, depois imprima a lista resultante

Como sua classe Range estende Iterable, ela ganha automaticamente acesso a todos os poderosos métodos de coleção como map, where e fold - essa é a beleza de implementar a interface Iterable!

Saída esperada:

1
2
3
4
5

[20, 22, 24, 26]

Folha de consulta

Um Iterable é qualquer objeto que pode produzir uma sequência de valores. Ele requer um getter iterator que retorna um Iterator.

Um Iterator percorre os elementos um a um usando:

  • moveNext() - avança para o próximo elemento e retorna true se houver um, ou false quando terminar
  • current - propriedade que fornece o elemento atual

Usando um iterator manualmente:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1, depois 2, depois 3
}

Para tornar uma classe personalizada iterável, implemente a interface Iterable e crie uma classe Iterator correspondente:

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

Classes que estendem Iterable funcionam com loops for-in e ganham acesso automaticamente a todos os métodos de Iterable, como map, where e fold.

Experimente você mesmo

import 'range.dart';

void main() {
  // TODO: Crie um Range(1, 5) e use um loop for-in para imprimir cada número
  
  
  // TODO: Imprima uma linha vazia
  
  
  // TODO: Crie um Range(10, 13), use map para dobrar cada valor e imprima a lista
  
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos