Java 8:Lambda 流,按方法筛选,但有异常

2022-08-31 06:18:58

我在尝试Java 8的Lambda表达式时遇到了问题。通常它工作正常,但现在我有了投掷的方法。最好查看以下代码:IOException

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

问题是,它不会编译,因为我必须捕获isActive-和getNumber-Methods的可能异常。但是,即使我显式使用如下所示的 try-catch-Block,它仍然没有编译,因为我没有捕获异常。因此,要么JDK中存在错误,要么我不知道如何捕获这些异常。

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

我怎样才能让它工作?有人可以提示我正确的解决方案吗?


答案 1

您必须在异常转义 lambda 之前捕获该异常:

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

考虑这样一个事实,即 lambda 不是在你编写它的地方计算的,而是在 JDK 类中某个完全不相关的位置计算的。因此,这将是引发选中异常的点,并且在该位置未声明它。

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

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());

在我的项目中,我处理这个问题而不包装;相反,我使用一种方法,有效地化解编译器对异常的检查。毋庸置疑,这应该谨慎处理,并且项目中的每个人都必须意识到,如果未声明,则可能会出现已检查的异常。这是管道代码:

public static <T> T uncheckCall(Callable<T> callable) {
    try {
        return callable.call();
    } catch (Exception e) {
        sneakyThrow(e);
        return null; // Unreachable but needed to satisfy compiler
    }
}

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扔在你的脸上,即使收集没有声明它。在大多数(但不是所有)现实生活中,无论如何,您都希望重新抛出异常,并将其作为一般故障进行处理。在所有这些情况下,在清晰度或正确性方面没有任何损失。只要提防那些其他情况,您实际上希望当场对异常做出反应。编译器不会让开发人员知道那里有一个要捕获的,如果你试图捕获它,编译器实际上会抱怨,因为我们欺骗了它,相信不能抛出这样的异常。IOException


答案 2

你还可以用 lambdas 传播你的静态痛苦,所以整个事情看起来是可读的:

s.filter(a -> propagate(a::isActive))

propagatehere 作为参数接收,并将调用期间捕获的任何异常转换为 。在番石榴中有一个类似的转换方法Throwables#propagate(Throwable)。java.util.concurrent.CallableRuntimeException

这种方法似乎对于lambda方法链接至关重要,所以我希望有一天它会被添加到一个流行的库中,或者这种传播行为将是默认的。

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}

推荐