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


172

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

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

现在,我想为A流所传递的每个实例调用foo :

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

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



Answers:


141

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

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

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

(超类型的例外Exception作为例子,切勿尝试自行抓住它)

然后,您可以使用调用它as.forEach(this::safeFoo)


1
如果您希望它成为包装方法,则可以将其声明为静态。它不使用“ this”中的任何内容。
aalku 2014年

206
令人遗憾的是,我们必须这样做而不是使用我们的本地异常。...哦,Java,它先给出然后取走
Erich

1
@Stephan该答案已被删除,但仍在此处提供:stackoverflow.com/a/27661562/309308
Michael Mrozek 2016年

3
那将立即使我公司的代码审查失败:不允许我们抛出未经检查的异常。
Stelios Adamantidis

7
@SteliosAdamantidis该规则的限制极其严格,适得其反。您是否知道所有api中的所有方法都被授权抛出所有类型的运行时异常,而您甚至不必知道(当然是)?您是否因为未实现检查异常的概念而禁止javascript?如果我是您的首席开发人员,那么我将禁止检查异常。
spi

35

如果您只想调用foo,并且希望按原样传播该异常(不使用换行),则也可以只使用Java的for循环(在将Stream转换为具有某些技巧的Iterable之后):

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

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


16

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

在我看来,您可以找到的最优雅的方式是Misha给出的 ,只需执行“未来”操作即可在Java 8流中聚合运行时异常。因此,您可以运行所有工作部件,并将不工作的异常收集为一个。否则,您可以将它们全部收集在一个列表中,并在以后进行处理。

本杰·韦伯Benji Weber)也有类似的方法。他建议创建一个自己的类型来收集工作部分而不是工作部分。

根据您真正想要在输入值和输出值之间实现简单映射的条件,异常可能也适用于您。

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


3
这应该标记为正确答案。+。但是我不能说这是一个好帖子。您应该对其进行详细说明。自己的异常有什么帮助?发生了什么异常?您为什么没有在这里带来不同的变体?在SO中,将所有示例代码都包含在引用中被视为甚至禁止发布的样式。您的文字看起来像是一堆评论。
Gangnus '17

2
您应该能够选择要捕获它们的级别,然后流将其弄乱。Stream API应该允许您携带异常,直到最终操作(如collect)为止,并使用处理程序在此处进行处理,否则将引发异常。stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...)。它期望例外,并允许您像期货一样处理它们。
aalku

10

我建议使用Google Guava Throwables类

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

4
不推荐使用此方法(不幸的是)。
罗伯特·瓦赞(RobertVažan)

9

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

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

9

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

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

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

图书馆做得好!我正要写类似的东西。
贾斯珀·德弗里斯

2

更具可读性的方式:

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

只需将其隐藏在A中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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.