关于java:CompletableFuture中的抛出异常

您所在的位置:网站首页 executionexception处理 关于java:CompletableFuture中的抛出异常

关于java:CompletableFuture中的抛出异常

2024-05-05 08:44| 来源: 网络整理| 查看: 265

我有以下代码:

123456789101112// How to throw the ServerException? public void myFunc() throws ServerException{     // Some code     CompletableFuture a = CompletableFuture.supplyAsync(() -> {         try {             return someObj.someFunc();         } catch(ServerException ex) {             // throw ex; gives an error here.         }     }));     // Some code }

someFunc()引发ServerException。我不想在这里处理此问题,但是将someFunc()的异常抛出给myFunc()的调用者。

您的代码建议您稍后以相同的方法使用异步操作的结果,因此无论如何您都必须处理CompletionException,因此处理它的一种方法是

12345678910111213141516171819202122232425public void myFunc() throws ServerException {     // Some code     CompletableFuture a = CompletableFuture.supplyAsync(() -> {         try { return someObj.someFunc(); }         catch(ServerException ex) { throw new CompletionException(ex); }     });     // Some code running in parallel to someFunc()     A resultOfA;     try {         resultOfA = a.join();     }     catch(CompletionException ex) {         try {             throw ex.getCause();         }         catch(Error|RuntimeException|ServerException possible) {             throw possible;         }         catch(Throwable impossible) {             throw new AssertionError(impossible);         }     }     // some code using resultOfA }

调用join时,在Supplier的异步处理中抛出的所有异常都将被package到CompletionException中,除了我们已经package在CompletionException中的ServerException。

重新抛出CompletionException的原因时,我们可能会遇到未检查的异常,即Error或RuntimeException的子类,或者我们的自定义检查的异常ServerException。上面的代码通过多次捕获来处理所有这些错误,然后将它们重新抛出。由于声明的getCause()返回类型为Throwable,因此尽管我们已经处理了所有可能的类型,但编译器仍要求我们处理该类型。直接的解决方案是将包裹在AssertionError中的实际上不可能的可抛出对象抛出。

或者,我们可以为自定义异常使用future的替代结果:

1234567891011121314151617181920212223public void myFunc() throws ServerException {     // Some code     CompletableFuture exception = new CompletableFuture();     CompletableFuture a = CompletableFuture.supplyAsync(() -> {         try { return someObj.someFunc(); }         catch(ServerException ex) {             exception.complete(ex);             throw new CompletionException(ex);         }     });     // Some code running in parallel to someFunc()     A resultOfA;     try {         resultOfA = a.join();     }     catch(CompletionException ex) {         if(exception.isDone()) throw exception.join();         throw ex;     }     // some code using resultOfA }

此解决方案将重新抛出所有意外结果。以package形式抛出的对象,但仅将自定义ServerException抛出为通过exception future传递的原始形式。请注意,在查询exception未来之前,必须确保a已完成(例如首先调用join()),以避免出现竞争情况。

相关讨论 非常详细的答案。 番石榴有帮助方法。捕获看起来像这样:Throwables.throwIfUnchecked(e.getCause());抛出新的RuntimeException(e.getCause()); @霍尔格出色的答案!一个需要阻塞联接以捕获并引发异步异常 @Holger:为什么不使用get()方法?那不是简单的多捕获块吗? @Miguel get与join的不同之处在于,将异常package在ExecutionException中而不是CompletionException中。这对catch端没有任何改进。它还要求调用者处理InterruptedException,这使其变得更加复杂。此外,由于Supplier不能抛出已检查的ExecutionException,因此必须保留CompletionException,然后get将提取原因并将其重新package在ExecutionException中,这样可以减少原因高效的。在特殊情况下,性能并不是很重要。但是get在这里的各个方面都比join差。

对于那些寻求通过completableFuture

处理异常的其他方式的人

以下是处理解析为整数的错误的几种方法:

1。使用handle方法-使您可以提供有关异常

的默认值

1234567891011121314CompletableFuture correctHandler = CompletableFuture.supplyAsync(() ->"A")             .thenApply(Integer::parseInt)             .handle((result, ex) -> {                 if (null != ex) {                     ex.printStackTrace();                     return 0;                 } else {                     System.out.println("HANDLING" + result);                     return result;                 }             })             .thenAcceptAsync(s -> {                 System.out.println("CORRECT:" + s);             });

2。使用exceptionally方法-与handle类似,但详细程度

123456CompletableFuture parser = CompletableFuture.supplyAsync(() ->"1")                 .thenApply(Integer::parseInt)                 .exceptionally(t -> {                     t.printStackTrace();                     return 0;                 }).thenAcceptAsync(s -> System.out.println("CORRECT value:" + s));

3。使用whenComplete方法-使用此方法将停止该方法的执行,而不会执行下一个thenAcceptAsync

12345678910CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() ->"A")                 .thenApply(Integer::parseInt)                 .whenComplete((result, ex) -> {                     if (null != ex) {                         ex.printStackTrace();                     }                 })                 .thenAcceptAsync(s -> {                     System.out.println("When Complete:" + s);                 });

4。通过completeExceptionally

传播异常

123456789public static CompletableFuture converter(String convertMe) {         CompletableFuture future = new CompletableFuture();         try {             future.complete(Integer.parseInt(convertMe));         } catch (Exception ex) {             future.completeExceptionally(ex);         }         return future;     } 相关讨论 对于像我这样的人,由于CompletableFuture而无法使用1、2和3,只需返回一个null即可使用Void类型的对象:)花了我几个小时来弄清楚这个简单事物。

即使其他人的回答非常好。但是我给您提供了另一种在CompletableFuture中引发检查异常的方法。

如果您不想在另一个线程中调用CompletableFuture,则可以使用匿名类来处理它,如下所示:

1234567CompletableFuture a = new CompletableFuture() {{     try {         complete(someObj.someFunc());     } catch (ServerException ex) {         completeExceptionally(ex);     } }};

如果要在另一个线程中调用CompletableFuture,则还可以使用匿名类来处理它,但可以通过runAsync:

运行方法

123456789CompletableFuture a = new CompletableFuture() {{     CompletableFuture.runAsync(() -> {         try {             complete(someObj.someFunc());         } catch (ServerException ex) {             completeExceptionally(ex);         }     }); }};

相关讨论 完全不需要在匿名子类中执行此操作。子类仅浪费资源。另请参见此处和此处。 @Holger先生,谢谢。我只是在脑海里写下来。我待会再见。 @Holger先生,我发现您的两个答案不同。我更喜欢您在此问题中使用的第一个。因为它易于使用且非常清晰。

我认为您应该将其package为RuntimeException并抛出:

1 throw new RuntimeException(ex);

或者成为一个小实用程序会有所帮助:

123456789101112131415161718192021222324252627static class Wrapper extends RuntimeException {     private Wrapper(Throwable throwable) {         super(throwable);     }     public static Wrapper wrap(Throwable throwable) {         return new Wrapper(throwable);     }     public Throwable unwrap() {         return getCause();     } }  public static void go() {     CompletableFuture a = CompletableFuture.supplyAsync(() -> {         try {             throw new Exception("Just because");         } catch (Exception ex) {             throw Wrapper.wrap(ex);         }     });     a.join(); }

然后您可以unwrap这样。.

12345 try {         go();  } catch (Wrapper w) {         throw w.unwrap();  } 相关讨论 我只需要抛出一个ServerException。 @ayushgp我看不到默认流发生这种情况,因为它们不允许检查异常...也许您可以package那个而不是解包吗? 看来您的go()方法永远不会抛出任何东西。我想它缺少一个join()调用。另外,Wrapper所提供的功能不及Throwable.getCause()所提供的。我不会在不设置原因的情况下将异常package到另一个异常中,因为它违反了约定并且不会打印正确的堆栈跟踪。 @DidierL是的go只是为了证明一个观点,确实不是很有用。另一方面,Wrapper只是将检查的异常package到运行时中。 @Eugene我的意思是,在当前形式的go()中,您的try / catch永远不会真正抓住Wrapper。我觉得这很令人误解。对于Wrapper,我的意思是应该调用super(Throwable)而不是定义自己的字段,以使printStackTrace()和getCause()的行为自然符合此类package程序的预期。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3