2

I have some code (with some more nested forEach's and streams):

void process() throws RuntimeException
{
    try {

        ArrayList<Integer> ints = new ArrayList<>();
        ints.add(1);
        ints.add(2);
        ints.add(3);

        ints.forEach(e -> {

            System.out.println(e);
            throw new RuntimeException("RuntimeException");

        });
    }
    catch (RuntimeException rex)
    {
        rex.printStackTrace();
        throw rex; // throw it up, and up, and up...
    }
}

It does not work because foreach's Consumer's accept() doesn't throw an exception by default. Even if it would have a throws signature - I couldn't catch it outside of the block.

What I need to do is to catch the exception from the foreach() method itself.

Is there any way I could achieve that without some external methods like

void handleException(RuntimeException ex){ ... }

and calling it in every forEach()'s try/catch ?

1 Answers1

2

I found that the question is wrong - it actually does work with RuntimeException.

For checked exception there's a working code:

package Exporter;

import java.util.function.Consumer;
import java.util.function.Function;

public final class LambdaExceptions {

    @FunctionalInterface
    public interface Consumer_WithExceptions<T, E extends Exception> {
        void accept(T t) throws E;
    }

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

    /**
     * .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name))));
     */
    public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
        return t -> {
            try {
                consumer.accept(t);
            } catch (Exception exception) {
                throwActualException(exception);
            }
        };
    }

    /**
     * .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
     */
    public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E  {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception exception) {
                throwActualException(exception);
                return null;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static <E extends Exception> void throwActualException(Exception exception) throws E {
        throw (E) exception;
    }

}

And it works like a charm:

void process() throws RuntimeException
{
    try {
        ArrayList<Integer> ints = new ArrayList<>();
        ints.add(1);
        ints.add(2);
        ints.add(3);

        ints.forEach(LambdaExceptions.rethrowConsumer(e -> {
            System.out.println(e);
            throw new RuntimeException("RuntimeException");
        }));

    }
    catch (RuntimeException rex)
    {
        System.out.println("Got first exception");
        rex.printStackTrace();
        throw rex;
    }
}
  • It works, but does it mask horrible design flaws in your program? – Kayaman Oct 18 '17 at 09:20
  • 1
    Does mark the horrible design of the API I use. To answer your comment on the main post, the reason I need to make workaround like this one is that the API provided by the external company throws RuntimeEx on each data fetch request. If I have nested calls, for instance a method which fetches folders, it's subfolders etc and something goes wrong inside of this method, I need to rollback the entire task. –  Oct 18 '17 at 09:23
  • I could replace each `foreach()` with a simple `for`, but it looks really unreadable. –  Oct 18 '17 at 09:24
  • If you say replacing it with a `for` is uglier than creating wrappers, I'll take your word for it. Just remember that beauty is in the eye of the beholder. – Kayaman Oct 18 '17 at 09:28