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

时间:2014-05-08 17:25:00

标签: java java-8 unhandled-exception java-stream

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

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

现在我想为像A的每个实例调用foo,如:

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

问题:如何正确处理异常?代码无法在我的机器上编译,因为我没有处理foo()可能抛出的异常。 throws Exception的{​​{1}}似乎在这里毫无用处。那是为什么?

6 个答案:

答案 0 :(得分:124)

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

正常的包装习惯用语如下:

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

(超类型异常Exception 用作示例,永远不要试图自己抓住它)

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

答案 1 :(得分:13)

这个问题可能有点旧,但因为我认为&#34;对&#34;这里的答案只有一种方法可以导致一些问题隐藏在代码中的问题。即使有一点Controversy,也存在Checked Exceptions。

我认为最优雅的方式是Misha在Aggregate runtime exceptions in Java 8 streams给出的 只需执行&#34;期货&#34;中的操作。因此,您可以运行所有工作部件,并将单独的异常工作收集起来。否则,您可以在列表中收集它们并稍后处理它们。

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

根据您真正想要实现的目标,输入值和输出值之间的简单映射可能也适用于例外情况。

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

答案 2 :(得分:9)

我建议使用Google Guava Throwables类

  

宣传 Throwable throwable)

     

如果它是一个传播,则按原样传播   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);
        }
    });
}

答案 3 :(得分:7)

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

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

答案 4 :(得分:7)

您可能希望执行以下操作之一:

  • 传播已检查的例外,
  • 将其包装并传播未经检查的异常,或
  • 抓住异常并停止传播。

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

答案 5 :(得分:1)

更易读的方式:

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