Vadzim Vadzim - 3 months ago 18
Java Question

How to iterate with foreach loop over java 8 stream

Suppose we try to apply to java 8 stream a lambda that could throw checked exception:

Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");

stream.forEach(s -> writer.append(s)); // Unhandled exception: java.io.IOException


This won't compile.

One workaround is to nest checked exception in
RuntimeException
but it complicates later exception handling and it's just ugly:

stream.forEach(s -> {
try {
writer.append(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
});


Alternative workaround could be to convert limited functional
forEach
to plain old foreach loop that is more friendly to checked exceptions.

But naive approaches fail:

for (String s : stream) { // for-each not applicable to expression type 'java.util.stream.Stream<java.lang.String>'
writer.append(s);
}

for (String s : stream.iterator()) { // foreach not applicable to type 'java.util.Iterator<java.lang.String>'
writer.append(s);
}


Update

A trick that answers this question was previosly posted at Why does Stream<T> not implement Iterable<T>? as side answer that doesn't really answer that question itself.
I think this is not enough to qualify this question as duplicate of that one because they ask different things.

Answer

By definition foreach loop requires an Iterable to be passed in.

It can be achieved with anonymous class:

    for (String s : new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return stream.iterator();
        }
    }) {
        writer.append(s);
    }

This can be simplified with lambda because Iterable is a functional interface:

    for (String s : (Iterable<String>) () -> stream.iterator()) {
        writer.append(s);
    }

This can be converted to method reference:

    for (String s : (Iterable<String>) stream::iterator) {
        writer.append(s);
    }

Explicit cast can be avoided with intermediate variable or method param:

    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        writer.append(s);
    }

There is also StreamEx library in maven central that features iterable streams and other perks out of the box.


Here is some most popular questions that provide workarounds on checked exception handling within lambdas and streams:

Java 8 Lambda function that throws exception?

Java 8: Lambda-Streams, Filter by Method with Exception

How can I throw CHECKED exceptions from inside Java 8 streams?

Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?

Kotlin ;)