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() 可能引发的异常。 bar
的 throws 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->mightThrowException(x))).catch(e->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:如何在流中使用异常抛出方法?的主要内容,如果未能解决你的问题,请参考以下文章