C'è un motivo per cui Iterator e Stream non implementano Iterable?

2

L'altro giorno stavo giocando con un esperimento e avevo un ciclo for qualcosa del genere:

for (Node node : children) {
  // do stuff with node ...
}

E poi l'ho cambiato per fare questo:

for (Node node : children.stream().filter(n -> n.isCreated())) {
  // do stuff with node ...
}

E ottenuto questo errore: "Può solo scorrere su un array o un'istanza di java.lang.Iterable" Questo riapre alcune vecchie ferite per me. Ecco cosa trovo sciocco qui. Stream (la classe che non implementa Iterable) ha un metodo iterator (). Sono solo nel pensare che questa sia una grande svista? C'è qualche ragione logica per cui non dovrebbe essere dichiarato implementare Iterable? Ho capito che la programmazione funzionale è interessante e tutto ma non può essere che la ragione sia cercare di incoraggiare le persone a passare tutti i loro cicli da a% chiamate forEach() .

Quindi la seconda parte di questo fastidio è che anche se fai questo:

for (Node node : children.stream().filter(n -> n.isCreated()).iterator()) {
  // do stuff with node ...
}

Hai lo stesso identico errore. Questo è un vecchio fastidio per me perché quando hanno introdotto la sintassi for (:), non aveva mai avuto senso che Iterator non fosse una delle cose che potevi usare insieme a Iterable e array. Ora, sembra ancora più odioso dato che tutto ciò che dovrebbe essere fatto per rendere Iterator implementabile Iterable è aggiungere il seguente metodo a Iterator:

default Iterator<E> iterator() {
  return this;
}

È abbastanza facile aggirare questi problemi con metodi come questo:

static <T> Iterable<T> iterable(final Stream<T> stream)
{
  return new Iterable<T>() {
    @Override
    public Iterator<T> iterator() {
      return stream.iterator();
    }
  };
}

static <T> Iterable<T> iterable(final Iterator<T> iterator)
{
  return new Iterable<T>() {
    @Override
    public Iterator<T> iterator() {
      return iterator;
    }
  };
}

Ma perché? Sembra sciocco doverlo fare. Qualcuno è a conoscenza del perché non è stato fatto o ha escogitato motivi legittimi per non rendere queste due interfacce Iterable?

    
posta JimmyJames 06.06.2017 - 18:25
fonte

1 risposta

6

Romperebbero il Principio di sostituzione di Liskov implementando Iterable<> .

Una parte importante del contratto Iterable<> che non è descrivibile in Java è che puoi eseguire iterazioni più volte . Né Iterator<>Stream<> hanno restrizioni simili, quindi Iterator<> s o Streams<> perfettamente accettabili che generano valori solo una volta, quando convertiti in Iterable<> dalla conversione proposta, interromperanno il codice che si aspetta di essere in grado di operare sui valori più volte.

es.

<T> Iterable<Pair<T, T>> crossProduct(Iterable<T> source)
{
    List<Pair<T, T>> result = new ArrayList<Pair<T, T>>();

    for (outer : source) {
        for (inner : source) {
            result.add(new Pair<T, T>(outer, inner));
        }
    }
}
    
risposta data 06.06.2017 - 20:55
fonte

Leggi altre domande sui tag