且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Java 8:Lambda-Streams,按带有异常的方法过滤

更新时间:2023-09-18 23:11:22

你必须在它转义 lambda 之前捕获异常:

You must catch the exception before it escapes the lambda:

s = s.filter(a -> {
    try {
        return a.isActive();
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
});

考虑这样一个事实,即 lambda 不是在您编写它的地方计算的,而是在 JDK 类中的某个完全不相关的地方计算的.所以那将是该检查异常将被抛出的地方,并且在那个地方它没有被声明.

Consider the fact that the lambda isn't evaluated at the place you write it, but at some completely unrelated place, within a JDK class. So that would be the point where that checked exception would be thrown, and at that place it isn't declared.

您可以使用 lambda 的包装器来处理它,该包装器将已检查的异常转换为未检查的异常:

You can deal with it by using a wrapper of your lambda that translates checked exceptions to unchecked ones:

public static <T> T uncheckCall(Callable<T> callable) {
    try {
        return callable.call();
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

你的例子应该写成

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());


在我的项目中,我没有包装就处理这个问题;相反,我使用了一种有效地消除了编译器对异常检查的方法.不用说,这应该小心处理,项目中的每个人都必须意识到检查异常可能出现在未声明的地方.这是管道代码:


In my projects I deal with this issue without wrapping; instead I use a method which effectively defuses compiler's checking of exceptions. Needless to say, this should be handled with care and everybody on the project must be aware that a checked exception may appear where it is not declared. This is the plumbing code:

public static <T> T uncheckCall(Callable<T> callable) {
    try {
        return callable.call();
    } catch (Exception e) {
        return sneakyThrow(e);
    }
}

public static void uncheckRun(RunnableExc r) {
    try {
        r.run();
    } catch (Exception e) {
        sneakyThrow(e);
    }
}

public interface RunnableExc {
    void run() throws Exception;
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
    throw (T) t;
}

并且您可以期望获得 IOException 扔在你的脸上,即使 collect 没有声明它.在大多数但不是全部现实生活中,无论如何,您只想重新抛出异常,并将其作为一般故障处理.在所有这些情况下,都不会丢失任何清晰度或正确性.请注意其他情况,在这些情况下,您实际上希望当场对异常做出反应.编译器不会让开发人员知道有一个 IOException 可以在那里捕获,如果您尝试捕获它,编译器实际上会抱怨,因为我们已经欺骗它相信没有这样的异常可以被抛出.

and you can expect to get an IOException thrown in your face, even though collect does not declare it. In most, but not all real-life cases you would want to just rethrow the exception, anyway, and handle it as a generic failure. In all those cases, nothing is lost in clarity or correctness. Just beware of those other cases, where you would actually want to react to the exception on the spot. The developer will not be made aware by the compiler that there is an IOException to catch there and the compiler will in fact complain if you try to catch it because we have fooled it into believing that no such exception can be thrown.