从CompletableFuture抛出exception

我有以下代码:

// 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()的exceptionsomeFunc() myFunc()调用者。

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

 public 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 } 

除了我们已经包含在CompletionExceptionServerException之外,在调用join时,在Supplier的异步处理中抛出的所有exception都将被包装到CompletionException

当我们重新抛出CompletionException的原因时,我们可能会遇到未经检查的exception,即ErrorRuntimeException子类,或者我们的自定义检查exceptionServerException 。 上面的代码使用multi-catch处理所有这些代码,它们将重新抛出它们。 由于getCause()的声明返回类型是Throwable ,编译器要求我们处理该类型,尽管我们已经处理了所有可能的类型。 直接的解决方案是抛出一个包含在AssertionError实际上不可能的throwable。

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

 public 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 } 

此解决方案将以其包装forms重新抛出所有“意外”throwable,但仅将自定义ServerException抛出其原始forms,通过exception将来传递。 请注意,在我们查询exception未来之前,我们必须确保已完成a(如首先调用join() ),以避免竞争条件。

我认为你应该将它包装成RuntimeException并抛出:

  throw new RuntimeException(ex); 

或许多是一个小实用程序会有所帮助:

 static 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那个……

  try { go(); } catch (Wrapper w) { throw w.unwrap(); } 

即使其他人的答案非常好。 但我给你另一种方法在CompletableFuture抛出一个检查过的exception。

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

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

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

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