技巧 | Java 8 Stream 中异常处理的4种方式

技巧 | Java 8 Stream 中异常处理的4种方式

Stream API 和 lambda 是 Java8以来对Java的重大改进。从那时起,我们可以使用更具有功能性的语法风格的代码。但是有个问题就是,我们使用了 lambda 表达式,那 lambda 中的异常该怎么处理呢。

大家都知道,不能直接在 lambda 中调用那些会抛出异常的方法,因为这样从编译上都通不过。所以我们需要捕获异常以使代码能够编译通过。

例如,我们可以在 lambda 中做一个简单的 try-catch 并将异常包装成一个 RuntimeException,如下面的代码所示,但这不是最好的方法。

 myList.stream().map(t -> {
            try {
                return doSomething(t);
            } catch (MyException e) {
                throw new RuntimeException(e);
            }
        }).forEach(System.out::println);

我们大多数人都知道,lambda 代码块是笨重的,可读性较差。在我看来,应该尽可能避免直接在 lambda 中使用大量的代码段。

如果我们在 lambda 表达式中需要做多行代码,那么我们可以把这些代码提取到一个单独的方法中,并简单地调用新方法。

所以,解决此问题的更好和更易读的方法是将调用包装在一个普通的方法中,该方法执行 try-catch 并从 lambda 中调用该方法,如下面的代码所示:

 myList.stream().map(this::trySomething).forEach(System.out::println);
        private T trySomething (T t){
            try {
                return doSomething(t);
            } catch (MyException e) {
                throw new RuntimeException(e);
            }
        }

这个解决方案至少有点可读性,并且将我们所关心的的问题也解决了。如果你真的想要捕获异常并做一些特定的事情而不是简单地将异常包装成一个 RuntimeException,那么这对你来说可能是一个还不错的解决方案。

一.包装成运行时异常

在许多情况下,你会看到大家都喜欢将异常包装成一个RuntimeException,或者是一个具体的未经检查的异常类。这样做的话,我们就可以在 lambda 内调用该方法。

如果你想把 lambda 中的每个可能抛出异常的调用都包装到 RuntimeException中,那你会看到很多重复的代码。为了避免一遍又一遍地重写相同的代码,我们可以将它抽象为一个方法,这样,你只需编写一次然后每次需要的时候直接调用他就可以了。

首先,你需要为函数编写自己的方法接口。只有这一次,你需要定义该函数可能抛出异常,例如下列所示:

 @FunctionalInterfacepublic
        interface CheckedFunction<T, R> {
            R apply(T t) throws Exception;
        }

现在,您可以编写自己的通用方法了,它将接受一个 CheckedFunction 参数。你可以在这个通用方法中处理 try-catch 并将原始异常包装到 RuntimeException中,如下列代码所示:

public static <T, R > Function < T, R > wrap(CheckedFunction < T, R > checkedFunction) {
            return t -> {
                try {
                    return checkedFunction.apply(t);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            };
        }

但是这种写法也是一个比较丑陋的 lambda 代码块,你可以选择要不要再对方法进行抽象。

通过简单的静态导入,你现在可以使用全新的通用方法来包装可能引发异常的lambda,如下列代码所示:

 myList.stream().map(wrap(t -> doSomething(t))).forEach(System.out::println);

剩下的唯一问题是,当发生异常时,你的 stream 处理会立即停止。如果你的业务可以容忍这种情况的话,那没问题,但是,我可以想象,在许多情况下,直接终止并不是最好的处理方式。

二.包装成 Either 类型

使用 stream 时,如果发生异常,我们可能不希望停止处理。

剩余70%内容付费后可查看
0