How can I throw CHECKED exceptions from inside Java 8 streams?

前端 未结 18 1861
你的背包
你的背包 2020-11-22 06:59

How can I throw CHECKED exceptions from inside Java 8 streams/lambdas?

In other words, I want to make code like this compile:

public List

        
18条回答
  •  故里飘歌
    2020-11-22 07:42

    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>> 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> l1, List> 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> instead of just a Stream. 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 CompletableFuture -- cfCollector() 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.

提交回复
热议问题