Java Iterator – Why Iterator and Stream Do Not Implement Iterable

iteratorjava

The other day I was playing around with an experiment and I had a for loop something like so:

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

And then I changed it to do this:

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

And got this error: Can only iterate over an array or an instance of java.lang.Iterable. This reopens some old wounds for me. Here's what I find silly here. Stream (the class that doesn't implement Iterable) has an iterator() method. Am I alone in thinking this is a major oversight? Is there any logical reason why it shouldn't be declared implement Iterable? I get that functional programming is cool and all but it can't be that the reason is to try to encourage people switch all their for loops to forEach() calls.

So the second part of this annoyance is that even if you do this:

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

You get the exact same error. This is an old annoyance for me because when they introduced the for(:) syntax, it never made sense that Iterator wasn't one of the things you could use along with Iterable and arrays. Now, it seems even more obnoxious given that all that would need to be done to make Iterator implement Iterable is add the following method to Iterator:

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

It's easy enough to work around these issues with methods like this:

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

Or with lambas:

Iterable<T> foo = () -> iterator;

But why? It just seems silly to have to do this. Is anyone aware of why this wasn't done or come up with any legitimate reasons for not making these two interfaces Iterable?

Best Answer

They would break the Liskov Substitution Principle implementing Iterable<>.

An important part of the Iterable<> contract that isn't describable in Java is that you can iterate multiple times. Neither Iterator<> nor Stream<> have similar restrictions, so perfectly acceptable Iterator<>s or Streams<> that generate values only once, when converted to Iterable<> by your proposed conversion, will break code that expects to be able to independently operate on the values multiple times.

E.g.

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