【问题标题】:Rethrow exception inside handleAsync() of CompletableFuture [duplicate]在 CompletableFuture 的 handleAsync() 中重新抛出异常 [重复]
【发布时间】:2021-06-30 12:50:35
【问题描述】:

我正在开发一个函数,该函数采用CompletableFuture<Object> 并需要处理其结果(或其异常)。 我正在修改它,以便在某种情况下,我需要抛出一个异常。

但是,编译器告诉我我没有处理这个新异常 (Unhandled exception ...)。

函数如下所示:

  • //<-- THIS IS NOT OK 行中是我刚刚添加的内容
  • //<-- THIS IS OK 行中已经存在

代码:

public void myFunction(CompletableFuture<Object> resultSupplier, boolean someCondition) {
    resultSupplier.handleAsync((result, throwable) -> {
        if (throwable != null) {
            //do something with the throwable
        } else {
            if (someCondition) {
                throw new Throwable("some throwable"); //<-- THIS IS NOT OK ("Unhandled exception: java.lang.Throwable")
            }
            try {
                //do something with the result which may raise an exception
            } catch (Throwable ex) {
                //do something in the catch
                throw ex; //<-- THIS IS OK
            } finally {
                //do something to finalize
            }
        }
        return null; //I don't actually need the future, just to execute the code above
    });
}

我在理解这一点时遇到了一些麻烦。 为什么编译器可以重新抛出 try 块内捕获的 throwable,但重新抛出我添加的 throwable 就不行?

我必须说我更了解编译错误(我在 BiFunction&lt;&gt; 内,所以我不能抛出已检查的异常)而不是编译器对 catch 块内的 throw ex 感到高兴,但主要是我只是想了解这里发生了什么以及为什么两者之间存在差异。

附:您可以将代码 sn-p 复制粘贴到 IDE 中以轻松重现问题。

【问题讨论】:

标签: java


【解决方案1】:

请参考@Slaw 的回答\评论以获得答案。

错误答案 原因是Throwable 是异常的基类。如果捕获代码仅处理 Exception 它不会捕获它。您要重新抛出的另一个可能被捕获Throwable,但可能是原始类型。

通常,一个好的静态代码分析会告诉你这两种情况都是不行的——不要抛出显式的Throwable 实例,也不要重新抛出捕获的实例;而只是 throw; 以多态方式使用它。

编辑

这是我发现的:

如果我改变你提到的那一行

throw new Throwable("some throwable");

收件人:

throw new Exception("some throwable");

编译器仍然是婴儿床。

如果我将代码更改为:

            try {
                 throw new Exception(); <=== change here
            } catch (Throwable ex) {
                //do something in the catch
                throw ex; //<-- THIS IS OK
            } finally {
                //do something to finalize
            }

我可以让代码编译的唯一方法是当我使用时

throw new CompletionException(throwable);

似乎CompletableFuture 总是与CompletionException 打交道,所以让它开心的唯一方法就是将你的投掷物包裹在CompletionException 中。

【讨论】:

  • 我不认为这是答案,即使我抛出一个非常具体的异常(而不是 Throwable),我仍然会遇到同样的问题。此外,即使我确实将第二个异常捕获为 Throwable 但我将它作为自己的类型重新抛出,编译器也无法知道我在抛出什么,因此它应该像第一个一样抱怨可投掷。
  • 附言。您适合静态代码分析,但在某些情况下,无论发生什么情况,捕获和重新抛出 Throwable 是确保您的数据库/缓存一致的唯一方法。这是其中一种情况(它是分布式服务的缓存,我不能重新抛出,否则我会在缓存中留下不一致的地方,而且我根本无法控制内部调用的不同方法会抛出什么) .
  • 非常感谢您抽出宝贵时间,但不幸的是它仍然没有回答我的问题 :) 我仍然觉得编译器的行为在这两种情况下不一致,但我希望它是一致的。
  • @MatteoNNZ 在您的 try-catch 块中,引发了什么异常?因为如果没有办法在try 块内抛出已检查异常,那么编译器可以推断catch 块内的throw ex 是有效的。即使您捕获Throwable,编译器也知道唯一可能的类型是unchecked 异常,因此允许抛出异常。但是,如果您要在 try 块内抛出已检查异常,或调用能够抛出已检查异常的方法,那么在这种情况下编译将失败(因为 BiFunction 不能抛出已检查异常)。跨度>
  • @Slaw 你是对的,这就是原因(刚刚测试过)。您可以将其写为答案,我会接受。奇怪的是编译器做了这个假设(即使这个假设是有道理的),我原以为它是从 catch 块声明中静态推断出类型(意思是 Throwable)
猜你喜欢
  • 1970-01-01
  • 2016-07-04
  • 1970-01-01
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多