【问题标题】:Java 8: How do I work with exception throwing methods in streams?Java 8:如何在流中使用异常抛出方法?
【发布时间】: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 java-8 unhandled-exception java-stream


【解决方案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);
        }
    });
}

【讨论】:

【解决方案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 测试中所做的事情,我不想费心包装检查的异常(实际上更喜欢我的测试抛出未包装的原始异常)

    【讨论】:

    【解决方案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 该答案已被删除,但仍可在此处找到:stackoverflow.com/a/27661562/309308
      • 这会立即导致我公司的代码审查失败:我们不允许抛出未经检查的异常。
      • @SteliosAdamantidis 该规则极其严格且适得其反。您是否知道所有 api 中的所有方法都被授权抛出所有类型的运行时异常,而您甚至不必知道它(当然您是)?您是否因为没有实现检查异常的概念而禁止了 javascript?如果我是你的首席开发人员,我会禁止检查异常。
      猜你喜欢
      • 1970-01-01
      • 2017-03-28
      • 1970-01-01
      • 1970-01-01
      • 2021-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多