1

A little backstory (ok, a lot): I've been working on creating classes that take java.util.function types and wrap their execution with try/catch blocks to remove the need for using try/catch from within lambda statements. Something that would allow this test code:

list.forEach(ExceptionWrapper.wrapConsumer(s -> {throw new Exception(s);}));

In doing so, I came up with this. It did not work.

public class ExceptionWrapper {
    public static <T> Consumer<T> wrapConsumer(Consumer<T> consumer){
        return t -> {
            try {
                consumer.accept(t);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

Many incantations always result in

Error:(54, 61) java: unreported exception java.lang.Exception; must be caught or declared to be thrown

After much searching, I came across the jOOλ code. It looked nearly the same except they use an intermediary lambda construct that mimics the wrapped types but declares to throw an exception. I made my own (did NOT copy the code, just the concept) and it works pretty awesome

public interface ConsumerWrapper<T>{
    void accept(T t) throws Exception;
}

and making the change in ExceptionWrapper

    public static <T> Consumer<T> wrapConsumer(ConsumerWrapper<T> consumer){
        ...                                        //  ^ note change
    }

allows the initial test code to compile and run.

public static void main(String[] args) {
    List<String> strings = Arrays.asList("1");
    strings.forEach(System.out::println);
    strings.forEach(ExceptionWrapper.wrapConsumer(s -> {throw new Exception(s);}));
}
Exception in thread "main" java.lang.RuntimeException: java.lang.Exception: 1
    at crap.unk.ExceptionWrapper.lambda$wrapConsumer$2(ExceptionWrapper.java:39)
    at crap.unk.ExceptionWrapper$$Lambda$3/1401420256.accept(Unknown Source)
...
Caused by: java.lang.Exception: 1
    at crap.unk.ExceptionWrapper.lambda$main$3(ExceptionWrapper.java:54)
    at crap.unk.ExceptionWrapper$$Lambda$2/501263526.accept(Unknown Source)

THE QUESTION

Why wouldn't my original attempt work? Why does the use of the intermediary, that has the identical structure, make it work? It seems caused by the difference in the throws clause but I don't see why. Why is it different than passing an Object off to a method and surrounding a call with a try/catch?
MadConan
  • 3,749
  • 1
  • 16
  • 27
  • 1
    Btw, you don't need to wrap exceptions, you can [throw checked exceptions as unchecked](http://stackoverflow.com/a/13528805/1362755). Add in a few converters for throwing equivalents of Consumer/Function/Supplier etc. and it should simplify stream processing – the8472 Jun 05 '15 at 20:45

1 Answers1

2

Your initial attempt at wrapConsumer didn't work because it still took a Consumer as a parameter, and your lambda expression that you attempted to wrap still threw an Exception -- a checked exception. Your try/catch is too far from the thrown exception, because at the point you create a lambda expression, you've created a Consumer there, and Consumer's accept method isn't declared to throw any checked exceptions.

The change to accepting a ConsumerWrapper works because that interface's method declaration allows it to throw Exception, with throws Exception. That allows you to create a lambda expression that throws a checked exception.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • "*at the point you create a lambda expression, you've created a Consumer there, and Consumer's accept method isn't declared to throw any checked exceptions.*" This is the statement that helped me. I think I see the compile flow now. I'm still getting used to thinking about the function constructs and how they are interpreted. – MadConan Jun 05 '15 at 20:29