我怎样才能从Java 8stream内引发CHECKEDexception? (不包装成未经检查的例外)

我怎样才能从Java 8stream/ lambdas中引发CHECKEDexception?

换句话说,我想编译这样的代码:

public List<Class> getClasses() throws ClassNotFoundException { List<Class> classes = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .map(className -> Class.forName(className)) .collect(Collectors.toList()); return classes; } 

此代码不会编译,因为上面的Class.forName()方法会抛出ClassNotFoundException

请注意,我不想将检查的exception包装在运行时exception中,而是抛出包装的未经检查的exception。 我想抛出检查exception本身 ,而不添加丑陋的try / catches到stream。

你的问题的简单答案是:你不能,至less不能直接。 这不是你的错。 甲骨文搞砸了。 他们坚持检查exception的概念,但在devisefunction接口,stream,lambda等时,却不一致地忘记了检查exception。这一切都是由像罗伯特·C·马丁(Robert C. Martin)这样的专家来完成的。

这实际上是API中的一个巨大的bug,也是语言规范中的一个小错误。

API中的错误在于,它没有提供转发检查exception的function,这对于函数式编程来说实际上是非常有意义的。 正如我在下面将要演示的那样,这样一个设施将很容易实现。

语言规范中的错误是,只要types参数仅用于types列表允许的情况( throws子句),它就不允许types参数推断types列表而不是单一types。

我们对于Java程序员的期望是下面的代码应该被编译:

 import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class CheckedStream { // List variant to demonstrate what we actually had before refactoring. public List<Class> getClasses(final List<String> names) throws ClassNotFoundException { final List<Class> classes = new ArrayList<>(); for (final String name : names) classes.add(Class.forName(name)); return classes; } // The Stream function which we want to compile. public Stream<Class> getClasses(final Stream<String> names) throws ClassNotFoundException { return names.map(Class::forName); } } 

但是,它给:

 cher@armor1:~/playground/Java/checkedStream$ javac CheckedStream.java CheckedStream.java:13: error: incompatible thrown types ClassNotFoundException in method reference return names.map(Class::forName); ^ 1 error 

当前定义函数接口的方式阻止了编译器转发exception – 没有声明告诉Stream.map() ,如果Function.apply() throws EStream.map() throws E

缺less的是通过检查exception的types参数的声明。 以下代码显示了如何使用当前语法实际声明这样的传递types参数。 除了下面讨论的标记行中的特殊情况外,此代码编译和行为如预期。

 import java.io.IOException; interface Function<T, R, E extends Throwable> { // Declare you throw E, whatever that is. R apply(T t) throws E; } interface Stream<T> { // Pass through E, whatever mapper defined for E. <R, E extends Throwable> Stream<R> map(Function<? super T, ? extends R, E> mapper) throws E; } class Main { public static void main(final String... args) throws ClassNotFoundException { final Stream<String> s = null; // Works: E is ClassNotFoundException. s.map(Class::forName); // Works: E is RuntimeException (probably). s.map(Main::convertClass); // Works: E is ClassNotFoundException. s.map(Main::throwSome); // Doesn't work: E is Exception. s.map(Main::throwSomeMore); // error: unreported exception Exception; must be caught or declared to be thrown } public static Class convertClass(final String s) { return Main.class; } static class FooException extends ClassNotFoundException {} static class BarException extends ClassNotFoundException {} public static Class throwSome(final String s) throws FooException, BarException { throw new FooException(); } public static Class throwSomeMore(final String s) throws ClassNotFoundException, IOException { throw new FooException(); } } 

throwSomeMore的情况下,我们希望看到IOException被错过,但实际上却错过了Exception

这并不完美,因为types推理似乎在寻找单一types,即使在例外的情况下。 因为types推断需要一个types,所以E需要parsing为ClassNotFoundExceptionIOException Exception一个通用super类。

需要调整types推断的定义,以便在types列表允许的情况下使用types参数( throws子句),编译器将查找多个types。 然后,编译器报告的exceptiontypes将与被引用方法的检查exception的原始throws声明一样具体,而不是单个全部捕获超types。

坏消息是,这意味着甲骨文搞砸了。 当然,它们不会破坏用户代码,但是将exceptiontypes参数引入现有的function接口将会明确地破坏使用这些接口的所有用户代码的编译。 他们将不得不发明一些新的语法糖来解决这个问题。

更糟糕的是,这个话题已经在2010年由Brian Goetz讨论了https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_java ,似乎这个问题被忽略了,所以我想知道Oracle在做什么。

这个LambdaExceptionUtil帮助器类允许您在Javastream中使用任何已检查的exception,如下所示:

 Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .map(rethrowFunction(Class::forName)) .collect(Collectors.toList()); 

注意Class::forName抛出ClassNotFoundException ,它被选中 。 stream本身也抛出ClassNotFoundException ,而不是一些包装uncheckedexception。

 public final class LambdaExceptionUtil { @FunctionalInterface public interface Consumer_WithExceptions<T, E extends Exception> { void accept(T t) throws E; } @FunctionalInterface public interface BiConsumer_WithExceptions<T, U, E extends Exception> { void accept(T t, U u) throws E; } @FunctionalInterface public interface Function_WithExceptions<T, R, E extends Exception> { R apply(T t) throws E; } @FunctionalInterface public interface Supplier_WithExceptions<T, E extends Exception> { T get() throws E; } @FunctionalInterface public interface Runnable_WithExceptions<E extends Exception> { void run() throws E; } /** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */ public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) { return t -> { try { consumer.accept(t); } catch (Exception exception) { throwAsUnchecked(exception); } }; } public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) { return (t, u) -> { try { biConsumer.accept(t, u); } catch (Exception exception) { throwAsUnchecked(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) { return t -> { try { return function.apply(t); } catch (Exception exception) { throwAsUnchecked(exception); return null; } }; } /** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */ public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) { return () -> { try { return function.get(); } catch (Exception exception) { throwAsUnchecked(exception); return null; } }; } /** uncheck(() -> Class.forName("xxx")); */ public static void uncheck(Runnable_WithExceptions t) { try { t.run(); } catch (Exception exception) { throwAsUnchecked(exception); } } /** uncheck(() -> Class.forName("xxx")); */ public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier) { try { return supplier.get(); } catch (Exception exception) { throwAsUnchecked(exception); return null; } } /** uncheck(Class::forName, "xxx"); */ public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) { try { return function.apply(t); } catch (Exception exception) { throwAsUnchecked(exception); return null; } } @SuppressWarnings ("unchecked") private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; } } 

关于如何使用它的很多其他示例(在静态导入LambdaExceptionUtil ):

 @Test public void test_Consumer_with_checked_exceptions() throws IllegalAccessException { Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className)))); Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .forEach(rethrowConsumer(System.out::println)); } @Test public void test_Function_with_checked_exceptions() throws ClassNotFoundException { List<Class> classes1 = Stream.of("Object", "Integer", "String") .map(rethrowFunction(className -> Class.forName("java.lang." + className))) .collect(Collectors.toList()); List<Class> classes2 = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .map(rethrowFunction(Class::forName)) .collect(Collectors.toList()); } @Test public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException { Collector.of( rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), StringJoiner::add, StringJoiner::merge, StringJoiner::toString); } @Test public void test_uncheck_exception_thrown_by_method() { Class clazz1 = uncheck(() -> Class.forName("java.lang.String")); Class clazz2 = uncheck(Class::forName, "java.lang.String"); } @Test (expected = ClassNotFoundException.class) public void test_if_correct_exception_is_still_thrown_by_method() { Class clazz3 = uncheck(Class::forName, "INVALID"); } 

注1:上面的LambdaExceptionUtil类的rethrow方法可以毫无畏惧地使用,并可以在任何情况下使用 。 非常感谢用户@PaoloC帮助解决了最后一个问题:现在,编译器会要求您添加抛出子句和所有东西,就好像您可以在Java 8stream上原生地抛出检查exception一样。


注2:上面的LambdaExceptionUtil类的uncheck方法是红利方法,如果你不想使用它们,可以安全地将它们从类中移除。 如果您确实使用了它们,请谨慎操作,而不要理解以下用例,优点/缺点和局限性:

如果你正在调用一个永远不会抛出它声明的exception的方法,你可以使用uncheck方法。 例如:new String(byteArr,“UTF-8”)抛出UnsupportedEncodingException,但是Java规范保证UTF-8始终存在。 在这里,抛出声明是一个麻烦,任何解决scheme,以最小的样板沉默它是值得欢迎的: String text = uncheck(() -> new String(byteArr, "UTF-8"));

如果你正在实现一个严格的接口,而你没有添加一个throws声明的选项,那么你可以使用uncheck方法,但是抛出一个exception是完全合适的。 包装一个exception只是为了获得抛出exception的特权,会导致一个带有虚假exception的堆栈跟踪,这些exception不会提供有关实际发生错误的信息。 一个很好的例子是Runnable.run(),它不会抛出任何检查的exception。

•在任何情况下,如果您决定使用uncheck方法,请注意抛出CHECKEDexception而不带throws子句的这两个后果:1)调用代码将无法通过名称来捕获它(如果尝试,编译器会说:Exception不会在相应的try语句中抛出)。 它会冒泡,并可能被一些“catch Exception”或“catch Throwable”在主程序循环中捕获,这可能是你想要的。 2)它违反了最less惊喜的原则:捕获RuntimeException将不能保证捕获所有可能的exception。 出于这个原因,我认为这不应该在框架代码中完成,而只能在完全控制的业务代码中完成。

您可以!

扩展@marcg的UtilException ,并在必要时添加throw E :这样, 编译器会要求你添加throw子句和所有东西,就像你可以在java 8的streamUtilException抛出checkedexception一样。

说明:只需在您的IDE中复制/粘贴LambdaExceptionUtil ,然后按照下面的LambdaExceptionUtilTest所示使用它。

 public final class LambdaExceptionUtil { @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; } } 

一些testing显示使用情况和行为:

 public class LambdaExceptionUtilTest { @Test(expected = MyTestException.class) public void testConsumer() throws MyTestException { Stream.of((String)null).forEach(rethrowConsumer(s -> checkValue(s))); } private void checkValue(String value) throws MyTestException { if(value==null) { throw new MyTestException(); } } private class MyTestException extends Exception { } @Test public void testConsumerRaisingExceptionInTheMiddle() { MyLongAccumulator accumulator = new MyLongAccumulator(); try { Stream.of(2L, 3L, 4L, null, 5L).forEach(rethrowConsumer(s -> accumulator.add(s))); fail(); } catch (MyTestException e) { assertEquals(9L, accumulator.acc); } } private class MyLongAccumulator { private long acc = 0; public void add(Long value) throws MyTestException { if(value==null) { throw new MyTestException(); } acc += value; } } @Test public void testFunction() throws MyTestException { List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList()); assertEquals(2, sizes.size()); assertEquals(4, sizes.get(0).intValue()); assertEquals(5, sizes.get(1).intValue()); } private Integer transform(String value) throws MyTestException { if(value==null) { throw new MyTestException(); } return value.length(); } @Test(expected = MyTestException.class) public void testFunctionRaisingException() throws MyTestException { Stream.of("ciao", null, "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList()); } } 

你不能安全地做到这一点。 你可以作弊,但是你的程序被破坏了,这将不可避免地回来咬人(应该是你,但是我们的作弊通常会炸毁其他人)。

这是一个稍微安全的方法来做到这一点(但我仍然不build议这样做。)

 class WrappedException extends RuntimeException { Throwable cause; WrappedException(Throwable cause) { this.cause = cause; } } static WrappedException throwWrapped(Throwable t) { throw new WrappedException(t); } try source.stream() .filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... }) ... } catch (WrappedException w) { throw (IOException) w.cause; } 

在这里,你正在做的是捕捉lambda中的exception,从streampipe道中抛出一个信号,指示计算exception失败,捕获信号并作用于该信号以抛出基本exception。 关键是你总是捕获综合exception,而不是允许一个检查exception泄漏出来,而不会声明抛出exception。

你不能。

不过,你可能想看看我的一个项目 ,它可以让你更容易地操纵这样的“扔lambda”。

在你的情况下,你将能够做到这一点:

 import static com.github.fge.lambdas.functions.Functions.wrap; final ThrowingFunction<String, Class<?>> f = wrap(Class::forName); List<Class> classes = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .map(f.orThrow(MyException.class)) .collect(Collectors.toList()); 

并捕获MyException

这是一个例子。 另一个例子是你可以。或.orReturn()一些默认值。

请注意,这仍然是一个正在进行的工作,更多的是来。 更好的名字,更多的function等

我写了一个扩展Stream API 的库 ,允许你抛出检查exception。 它使用布赖恩·戈茨的诡计。

你的代码会变成

 public List<Class> getClasses() throws ClassNotFoundException { Stream<String> classNames = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String"); return ThrowingStream.of(classNames, ClassNotFoundException.class) .map(Class::forName) .collect(Collectors.toList()); } 

只要使用NoException (我的项目)中的任何一个, jOOλ的Unchecked , throwing-lambdas , Throwable接口或者Faux Pas 。

 // NoException stream.map(Exceptions.sneak().function(Class::forName)); // jOOλ stream.map(Unchecked.function(Class::forName)); // throwing-lambdas stream.map(Throwing.function(Class::forName).sneakyThrow()); // Throwable interfaces stream.map(FunctionWithThrowable.aFunctionThatUnsafelyThrowsUnchecked(Class::forName)); // Faux Pas stream.map(FauxPas.throwingFunction(Class::forName)); 

总结高级解决scheme的上面的注释是使用一个特殊的包装器,像API那样的构build器提供恢复,重新抛出和抑制的未经检查的function。

 Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") .map(Try.<String, Class<?>>safe(Class::forName) .handle(System.out::println) .unsafe()) .collect(toList()); 

下面的代码演示了消费者,供应商和function界面。 它可以很容易地扩大。 本例中删除了一些公开的关键字。

Try是客户端代码的端点。 每种functiontypes的安全方法可能具有唯一的名称。 CheckedConsumerCheckedSupplierCheckedFunction是lib函数的类比函数,可以独立于Try

CheckedBuilder是处理某些checked函数中的exception的接口。 如果以前失败, orTry允许执行另一个相同types的函数。 句柄提供exception处理,包括exceptiontypes过滤。 处理程序的顺序很重要。 减less方法不安全重新抛出重新抛出执行链中的最后一个exception。 如果所有函数失败,则减less方法orEseorElseGet返回可选值。 也有方法抑制CheckedWrapper是CheckedBuilder的常用实现。

 final class Try { public static <T> CheckedBuilder<Supplier<T>, CheckedSupplier<T>, T> safe(CheckedSupplier<T> supplier) { return new CheckedWrapper<>(supplier, (current, next, handler, orResult) -> () -> { try { return current.get(); } catch (Exception ex) { handler.accept(ex); return next.isPresent() ? next.get().get() : orResult.apply(ex); } }); } public static <T> Supplier<T> unsafe(CheckedSupplier<T> supplier) { return supplier; } public static <T> CheckedBuilder<Consumer<T>, CheckedConsumer<T>, Void> safe(CheckedConsumer<T> consumer) { return new CheckedWrapper<>(consumer, (current, next, handler, orResult) -> t -> { try { current.accept(t); } catch (Exception ex) { handler.accept(ex); if (next.isPresent()) { next.get().accept(t); } else { orResult.apply(ex); } } }); } public static <T> Consumer<T> unsafe(CheckedConsumer<T> consumer) { return consumer; } public static <T, R> CheckedBuilder<Function<T, R>, CheckedFunction<T, R>, R> safe(CheckedFunction<T, R> function) { return new CheckedWrapper<>(function, (current, next, handler, orResult) -> t -> { try { return current.applyUnsafe(t); } catch (Exception ex) { handler.accept(ex); return next.isPresent() ? next.get().apply(t) : orResult.apply(ex); } }); } public static <T, R> Function<T, R> unsafe(CheckedFunction<T, R> function) { return function; } @SuppressWarnings ("unchecked") static <T, E extends Throwable> T throwAsUnchecked(Throwable exception) throws E { throw (E) exception; } } @FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> { void acceptUnsafe(T t) throws Exception; @Override default void accept(T t) { try { acceptUnsafe(t); } catch (Exception ex) { Try.throwAsUnchecked(ex); } } } @FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> { R applyUnsafe(T t) throws Exception; @Override default R apply(T t) { try { return applyUnsafe(t); } catch (Exception ex) { return Try.throwAsUnchecked(ex); } } } @FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> { T getUnsafe() throws Exception; @Override default T get() { try { return getUnsafe(); } catch (Exception ex) { return Try.throwAsUnchecked(ex); } } } interface ReduceFunction<TSafe, TUnsafe, R> { TSafe wrap(TUnsafe current, Optional<TSafe> next, Consumer<Throwable> handler, Function<Throwable, R> orResult); } interface CheckedBuilder<TSafe, TUnsafe, R> { CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next); CheckedBuilder<TSafe, TUnsafe, R> handle(Consumer<Throwable> handler); <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handle( Class<E> exceptionType, Consumer<E> handler); CheckedBuilder<TSafe, TUnsafe, R> handleLast(Consumer<Throwable> handler); <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handleLast( Class<E> exceptionType, Consumer<? super E> handler); TSafe unsafe(); TSafe rethrow(Function<Throwable, Exception> transformer); TSafe suppress(); TSafe orElse(R value); TSafe orElseGet(Supplier<R> valueProvider); } final class CheckedWrapper<TSafe, TUnsafe, R> implements CheckedBuilder<TSafe, TUnsafe, R> { private final TUnsafe function; private final ReduceFunction<TSafe, TUnsafe, R> reduceFunction; private final CheckedWrapper<TSafe, TUnsafe, R> root; private CheckedWrapper<TSafe, TUnsafe, R> next; private Consumer<Throwable> handlers = ex -> { }; private Consumer<Throwable> lastHandlers = ex -> { }; CheckedWrapper(TUnsafe function, ReduceFunction<TSafe, TUnsafe, R> reduceFunction) { this.function = function; this.reduceFunction = reduceFunction; this.root = this; } private CheckedWrapper(TUnsafe function, CheckedWrapper<TSafe, TUnsafe, R> prev) { this.function = function; this.reduceFunction = prev.reduceFunction; this.root = prev.root; prev.next = this; } @Override public CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next) { return new CheckedWrapper<>(next, this); } @Override public CheckedBuilder<TSafe, TUnsafe, R> handle( Consumer<Throwable> handler) { handlers = handlers.andThen(handler); return this; } @Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handle(Class<E> exceptionType, Consumer<E> handler) { handlers = handlers.andThen(ex -> { if (exceptionType.isInstance(ex)) { handler.accept(exceptionType.cast(ex)); } }); return this; } @Override public CheckedBuilder<TSafe, TUnsafe, R> handleLast( Consumer<Throwable> handler) { lastHandlers = lastHandlers.andThen(handler); return this; } @Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handleLast(Class<E> exceptionType, Consumer<? super E> handler) { lastHandlers = lastHandlers.andThen(ex -> { if (exceptionType.isInstance(ex)) { handler.accept(exceptionType.cast(ex)); } }); return this; } @Override public TSafe unsafe() { return root.reduce(ex -> Try.throwAsUnchecked(ex)); } @Override public TSafe rethrow(Function<Throwable, Exception> transformer) { return root.reduce(ex -> Try.throwAsUnchecked(transformer.apply(ex))); } @Override public TSafe suppress() { return root.reduce(ex -> null); } @Override public TSafe orElse(R value) { return root.reduce(ex -> value); } @Override public TSafe orElseGet(Supplier<R> valueProvider) { Objects.requireNonNull(valueProvider); return root.reduce(ex -> valueProvider.get()); } private TSafe reduce(Function<Throwable, R> orResult) { return reduceFunction.wrap(function, Optional.ofNullable(next).map(p -> p.reduce(orResult)), this::handle, orResult); } private void handle(Throwable ex) { for (CheckedWrapper<TSafe, TUnsafe, R> current = this; current != null; current = current.next) { current.handlers.accept(ex); } lastHandlers.accept(ex); } } 

This answer is similar to 17 but avoiding wrapper exception definition:

 List test = new ArrayList(); try { test.forEach(obj -> { //let say some functionality throws an exception try { throw new IOException("test"); } catch(Exception e) { throw new RuntimeException(e); } }); } catch (RuntimeException re) { if(re.getCause() instanceof IOException) { //do your logic for catching checked } else throw re; // it might be that there is real runtime exception } 

I use this kind of wrapping exception:

 public class CheckedExceptionWrapper extends RuntimeException { ... public <T extends Exception> CheckedExceptionWrapper rethrow() throws T { throw (T) getCause(); } } 

It will require handling these exceptions statically:

 void method() throws IOException, ServletException { try { list.stream().forEach(object -> { ... throw new CheckedExceptionWrapper(e); ... }); } catch (CheckedExceptionWrapper e){ e.<IOException>rethrow(); e.<ServletExcepion>rethrow(); } } 

Though exception will be anyway re-thrown during first rethrow() call (oh, Java generics…), this way allows to get a strict statical definition of possible exceptions (requires to declare them in throws ). And no instanceof or something is needed.

I agree with the comments above, in using Stream.map you are limited to implementing Function which doesn't throw Exceptions.

You could however create your own FunctionalInterface that throws as below..

 @FunctionalInterface public interface UseInstance<T, X extends Throwable> { void accept(T instance) throws X; } 

then implement it using Lambdas or references as shown below.

 import java.io.FileWriter; import java.io.IOException; //lambda expressions and the execute around method (EAM) pattern to //manage resources public class FileWriterEAM { private final FileWriter writer; private FileWriterEAM(final String fileName) throws IOException { writer = new FileWriter(fileName); } private void close() throws IOException { System.out.println("close called automatically..."); writer.close(); } public void writeStuff(final String message) throws IOException { writer.write(message); } //... public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException { final FileWriterEAM writerEAM = new FileWriterEAM(fileName); try { block.accept(writerEAM); } finally { writerEAM.close(); } } public static void main(final String[] args) throws IOException { FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet")); FileWriterEAM.use("eam2.txt", writerEAM -> { writerEAM.writeStuff("how"); writerEAM.writeStuff("sweet"); }); FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt); } void writeIt() throws IOException{ this.writeStuff("How "); this.writeStuff("sweet "); this.writeStuff("it is"); } } 

The only built-in way of handling checked exceptions that can be thrown by a map operation is to encapsulate them within a CompletableFuture . (An Optional is a simpler alternative if you don't need to preserve the exception.) These classes are intended to allow you to represent contingent operations in a functional way.

A couple of non-trivial helper methods are required, but you can arrive at code that's relatively concise, while still making it apparent that your stream's result is contingent on the map operation having completed successfully. Here's what it looks like:

  CompletableFuture<List<Class<?>>> classes = Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double") .map(MonadUtils.applyOrDie(Class::forName)) .map(cfc -> cfc.thenApply(Class::getSuperclass)) .collect(MonadUtils.cfCollector(ArrayList::new, List::add, (List<Class<?>> l1, List<Class<?>> l2) -> { l1.addAll(l2); return l1; }, x -> x)); classes.thenAccept(System.out::println) .exceptionally(t -> { System.out.println("unable to get class: " + t); return null; }); 

This produces the following output:

 [class java.lang.Object, class java.lang.Number, class java.lang.Number] 

The applyOrDie method takes a Function that throws an exception, and converts it into a Function that returns an already-completed CompletableFuture — either completed normally with the original function's result, or completed exceptionally with the thrown exception.

The second map operation illustrates that you've now got a Stream<CompletableFuture<T>> instead of just a Stream<T> . CompletableFuture takes care of only executing this operation if the upstream operation succeeded. The API makes this explict, but relatively painless.

Until you get to the collect phase, that is. This is where we require a pretty significant helper method. We want to "lift" a normal collection operation (in this case, toList() ) "inside" the CompletableFuturecfCollector() lets us do that using a supplier , accumulator , combiner , and finisher that don't need to know anything at all about CompletableFuture .

The helper methods can be found on GitHub in my MonadUtils class, which is very much still a work in progress.