Checked exceptions

The action can throw a checked exception. It reaches the caller unchanged, and the loop stops.

Why it matters

IntStream.range(0, n).forEach(...) takes an IntConsumer, which cannot throw checked exceptions. The usual workaround wraps and unwraps.

STREAMS
IntStream.range(0, 3).forEach(i -> {
    try {
        Files.createFile(path(i));
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
});
JAVA-REPEAT
repeat(3, i -> Files.createFile(path(i)));
// throws IOException

How the type is inferred

repeat is declared <E extends Throwable> ... throws E. The compiler picks E from what the lambda throws:

  • Throws nothing checked: E is RuntimeException; nothing to declare.
  • Throws one checked type: E is that type, for example IOException.
  • Throws several: E is their common supertype, often Exception. Catch inside the action if you need them apart.

A failure stops the loop

repeat(5, i -> {
    ran.add(i);
    if (i == 1) throw new IOException("ERROR!");
});
// ran == [0, 1]; the IOException reaches the caller

Keep going after a failure

To run every iteration regardless, catch inside the action and collect what failed.

List<IOException> failures = new ArrayList<>();
repeat(5, i -> {
    try {
        Files.createFile(path(i));
    } catch (IOException e) {
        failures.add(e);
    }
});

Edit this page on GitHub