How can I re-throw an exception in a lambda block as from the outer block?

南楼画角 提交于 2019-12-19 03:15:12

问题


With following code,

void key(Key) throws SomeCheckedException {
}

void supplier(Supplier<Key> s) throws SomeCheckedException {
    ofNullable(s).ifPresent(s -> {                   //    |
        try {                                        //    |
            key(s.get());                            //    |
        } catch (final SomeCheckedException sce) {   //    |
            // sce is coming from key() method       //    |
            // How can I throw sce for outer method? //  --/
        }
    });
}

How can I throw sce as if the method(supplier) method is throwing it?

Please note that above code is just an example. I need the key(s.get()) to be inside a lambda expression.

void supplier(Supplier<Key> s) throws SomeCheckException {
    key(s.get());
}

回答1:


If you want to process checked exceptions in a safe manner you need a helper method which provides the facility of wrapping the exception into a sub-type of RuntimeException. Here is such a helper function which uses Generic’s type safety to ensure that only declared exceptions will be re-thrown (unless you use an unsafe operation):

public static <E extends Throwable> void attempt(
    Consumer<Function<E,RuntimeException>> action) throws E {

    final class CarryException extends RuntimeException {
        final E carried;
        CarryException(E cause) {
            super(cause);
            carried=cause;
        }
    }

    try { action.accept( CarryException::new ); }
    catch(CarryException ex) { throw ex.carried; }
}

It supports an arbitrary action which will receive a function which does the temporary wrapping of the checked exception type to the RuntimeException. This wrapping will be transparent, the method attempt will either complete normally or throw the original checked exception E (or an unrelated unchecked exception if one occurs).

So you can use it like this:

public static void processIterm(Supplier<Key> s)
    throws SomeCheckedException  {

    attempt( (Function<SomeCheckedException, RuntimeException> thrower) ->
        Optional.ofNullable(s).ifPresent(nonNull -> {
            try { key(nonNull.get()); } // assuming key may throw SomeCheckedException
            catch(SomeCheckedException  e) { throw thrower.apply(e); }
        }));
}

Due to the nested operations the compiler is unable to infer the exception type automatically. The code above use an explicit declaration of the thrower parameter type. Alternatively you can use a type invocation of the helper method like

ContainingClass.<SomeCheckedException>attempt( thrower ->
    Optional.ofNullable(s).ifPresent(nonNull -> {
        try { key(nonNull.get()); }
        catch(SomeCheckedException  e) { throw thrower.apply(e); }
    }));



回答2:


You can't. Supplier#get() does not declare to throw any (checked) exceptions. Remember that a lambda expression simply creates an instance, it doesn't actually invoke the target functional interface method.

If you want to, you can wrap the checked exception in an unchecked exception and throw that.



来源:https://stackoverflow.com/questions/27896035/how-can-i-re-throw-an-exception-in-a-lambda-block-as-from-the-outer-block

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!