Java 8:如何在流中使用异常抛出方法?

Posted

技术标签:

【中文标题】Java 8:如何在流中使用异常抛出方法?【英文标题】:Java 8: How do I work with exception throwing methods in streams? 【发布时间】:2014-06-26 05:56:52 【问题描述】:

假设我有一个类和一个方法

class A 
  void foo() throws Exception() 
    ...
  

现在我想为A 的每个实例调用 foo,这些实例由如下流传递:

void bar() throws Exception 
  Stream<A> as = ...
  as.forEach(a -> a.foo());

问题:如何正确处理异常?该代码无法在我的机器上编译,因为我不处理 foo() 可能引发的异常。 barthrows Exception 在这里似乎没用。这是为什么呢?

【问题讨论】:

Java 8: Lambda-Streams, Filter by Method with Exception 的可能重复项 相关:***.com/q/30117134/435605 使用Zalando's faux-pas library。 【参考方案1】:

我建议使用 Google Guava Throwables 类

传播可投掷可投掷)

如果它是可抛出的,则按原样传播 RuntimeException 或 Error 的实例,或者作为最后的手段,包装 它在 RuntimeException 中,然后传播。**

void bar() 
    Stream<A> as = ...
    as.forEach(a -> 
        try 
            a.foo()
         catch(Exception e) 
            throw Throwables.propagate(e);
        
    );

更新:

现在它已被弃用:

void bar() 
    Stream<A> as = ...
    as.forEach(a -> 
        try 
            a.foo()
         catch(Exception e) 
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        
    );

【讨论】:

这个方法是deprecated(很遗憾)。【参考方案2】:

更易读的方式:

class A 
  void foo() throws MyException() 
    ...
  

只需将其隐藏在 RuntimeException 中即可使其通过 forEach()

  void bar() throws MyException 
      Stream<A> as = ...
      try 
          as.forEach(a -> 
              try 
                  a.foo();
               catch(MyException e) 
                  throw new RuntimeException(e);
              
          );
       catch(RuntimeException e) 
          throw (MyException) e.getCause();
      
  

虽然此时我不会反对某人,如果他们说跳过流并使用 for 循环,除非:

您没有使用 Collection.stream() 创建流,即没有直接转换为 for 循环。 您正在尝试使用parallelstream()

【讨论】:

【参考方案3】:

您可能想要执行以下操作之一:

传播检查异常, 包装它并传播未经检查的异常,或者 捕获异常并停止传播。

Several libraries 让您轻松做到这一点。下面的示例是使用我的 NoException 库编写的。

// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));

// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));

// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));

【讨论】:

图书馆干得好!我正要写类似的东西。【参考方案4】:

这个问题可能有点老了,但是因为我认为这里的“正确”答案只是一种可能导致代码中隐藏问题的方法。即使有一点Controversy,Checked Exceptions 的存在也是有原因的。

在我看来你能找到的最优雅的方式是 Misha 在这里Aggregate runtime exceptions in Java 8 streams 只需执行“期货”中的操作。因此,您可以运行所有工作部分并将不工作的异常收集为一个。否则,您可以将它们全部收集到一个列表中,然后再进行处理。

类似的方法来自Benji Weber。他建议创建一个自己的类型来收集工作和不工作的部分。

根据您真正想要实现的输入值和输出值之间的简单映射,发生的异常也可能对您有用。

如果您不喜欢这些方式中的任何一种,请考虑使用(取决于原始异常)至少一个自己的异常。

【讨论】:

这应该被标记为正确答案。 +。但我不能说这是一个好帖子。你应该详细说明它。自己的例外有什么帮助?发生了什么异常?你为什么不把不同的变种带到这里来?在引用中包含所有示例代码在 SO 上被认为是一种甚至被禁止的帖子样式。您的文本看起来更像是一堆 cmets。 您应该能够选择要在哪个级别捕获它们并且流与此混淆。 Stream API 应该让您在最终操作(如收集)之前携带异常,并使用处理程序在那里处理或以其他方式抛出。 stream.map(Streams.passException(x-&gt;mightThrowException(x))).catch(e-&gt;whatToDo(e)).collect(...)。它应该期待异常并允许您像期货一样处理它们。【参考方案5】:

如果您只想调用foo,并且您更喜欢按原样传播异常(不包装),您也可以只使用Java 的for 循环(在将Stream 转换为带有一些@ 的Iterable 之后) 987654321@):

for (A a : (Iterable<A>) as::iterator) 
   a.foo();

这至少是我在 JUnit 测试中所做的事情,我不想费心包装检查的异常(实际上更喜欢我的测试抛出未包装的原始异常)

【讨论】:

您也可以使用传统的 for 循环,例如for (Iterator it = astream.iterator() ; it.hasNext() ;) it.next().foo(); 【参考方案6】:

您可以通过这种方式包装和解包异常。

class A 
    void foo() throws Exception 
        throw new Exception();
    
;

interface Task 
    void run() throws Exception;


static class TaskException extends RuntimeException 
    private static final long serialVersionUID = 1L;
    public TaskException(Exception e) 
        super(e);
    


void bar() throws Exception 
      Stream<A> as = Stream.generate(()->new A());
      try 
        as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
     catch (TaskException e) 
        throw (Exception)e.getCause();
    


static void wrapException(Task task) 
    try 
        task.run();
     catch (Exception e) 
        throw new TaskException(e);
    

【讨论】:

【参考方案7】:

您需要将您的方法调用包装到另一个不抛出检查异常的方法调用中。你仍然可以抛出任何 RuntimeException 的子类。

一个普通的包装习惯是这样的:

private void safeFoo(final A a) 
    try 
        a.foo();
     catch (Exception ex) 
        throw new RuntimeException(ex);
    

(超类型异常Exception用作示例,切勿尝试自己捕获)

然后你可以调用它:as.forEach(this::safeFoo)

【讨论】:

如果你希望它是一个包装方法,我会声明它是静态的。它不使用“this”中的任何内容。 很遗憾,我们不得不这样做而不是使用我们的原生异常......哦,Java,它先给出然后又带走 @Stephan 该答案已被删除,但仍可在此处找到:***.com/a/27661562/309308 这会立即导致我公司的代码审查失败:我们不允许抛出未经检查的异常。 @SteliosAdamantidis 该规则极其严格且适得其反。您是否知道所有 api 中的所有方法都被授权抛出所有类型的运行时异常,而您甚至不必知道它(当然您是)?您是否因为没有实现检查异常的概念而禁止了 javascript?如果我是你的首席开发人员,我会禁止检查异常。

以上是关于Java 8:如何在流中使用异常抛出方法?的主要内容,如果未能解决你的问题,请参考以下文章

Dart:让流中抛出的异常传播并在调用者中捕获它

如何在流中使用来自另一个流数据的数据? (科特林流程)

java 按方法值在流中查找对象

java的io流中,啥时候应该在流关闭之前加flush,啥时候不用

如何在流中键入检查日期对象?

Java 8 流中的 LINQ Enumerable.Single() [重复]