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

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

现在我想调用foo的每个实例的A交付的流,如:

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

问:如何正确地处理异常?代码不能在我的机器上编译,因为我没有处理可能由foo()抛出的异常。bar的抛出异常在这里似乎毫无用处。为什么呢?


当前回答

更易读的方式:

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

其他回答

更易读的方式:

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

如果你只想调用foo,并且你更喜欢传播异常(不包装),你也可以只使用Java的for循环(在使用一些技巧将流转换为Iterable之后):

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

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

您需要将您的方法调用包装到另一个方法调用中,在该方法调用中不抛出受控异常。您仍然可以抛出任何RuntimeException的子类。

一个正常的包装习语是这样的:

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

(超类型异常异常仅作为示例,永远不要尝试自己捕获它)

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

这个问题可能有点老了,但因为我认为这里的“正确”答案只有一种方式,这可能会导致一些问题隐藏在您的代码后面。即使有一点争议,受控异常的存在也是有原因的。

在我看来,你能找到的最优雅的方法是Misha在这里给出的聚合Java 8流中的运行时异常 通过在“期货”中执行操作。因此,您可以运行所有工作的部分,并将不工作的异常作为单个异常收集。否则,您可以将它们全部收集到一个List中,稍后再处理它们。

本吉·韦伯(Benji Weber)也提出了类似的方法。他建议创建一个自己的类型来收集工作和不工作的部分。

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

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

你可能想做以下其中一件事:

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

有几个库可以让您轻松地做到这一点。下面的示例是使用我的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)));