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

新手上路,请多包涵

我在尝试 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){
        }
    }
    ....
}

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

原文由 Martin Weber 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 807
2 个回答

您必须在它转义 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 扔在你的脸上,即使 collect 没有声明它。在 _大多数(但不是所有_)现实生活中,无论如何,您都只想重新抛出异常,并将其作为一般故障处理。在所有这些情况下,清晰或正确不会丢失任何内容。请注意其他情况,您实际上希望在现场对异常做出反应。编译器不会让开发人员知道有一个 IOException 可以捕捉到那里,如果你试图捕捉它,编译器实际上会抱怨,因为我们已经欺骗它相信不会有这样的异常抛出。

原文由 Marko Topolnik 发布,翻译遵循 CC BY-SA 4.0 许可协议

您还可以使用 lambda 传播您的静态疼痛,因此整个内容看起来可读:

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

propagate 此处接收 java.util.concurrent.Callable 作为参数,并将调用期间捕获的任何异常转换为 RuntimeException 。 Guava中有类似的转换方法 Throwables#propagate(Throwable)

这个方法似乎对于 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);
    }
}

原文由 Andrey Chaschev 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题