如何在代码抛出异常时使用 Java 8 CompletableFuture DRY 异常处理?

How to DRY exception handling with Java 8 CompletableFuture when code throws exception?

我不了解你们,但对我来说,当我看到一段代码 repeated 并且在使用 Services 时遇到以下情况时,它变得非常烦人抛出异常。如下所示,在每个 CompletableFuture 块中,我必须执行 exception handling 并且该部分基本上一遍又一遍地重复,具体取决于您将拥有多少可完成的期货。

CompletableFuture<Void> future1Of15 = CompletableFuture.supplyAsync(() -> {
    List<SomePojo> somePojos = null;
    try {
        somePojos = someServiceThatThrowsException.getAll(SomePojo.class);
    } catch (SomeException e) {
       //Handle the exception
       e.printStackTrace();
    } 
    return somePojos; 
}).thenAcceptAsync(result -> //do something with the result); 

CompletableFuture<Void> future2Of15 = CompletableFuture.supplyAsync(() -> {
    List<OtherPojo> otherPojos = null;
    try {
        otherPojos = someServiceThatThrowsException.getAll(OtherPojo.class);
    } catch (SomeException e) {
           //Handle the exception
           e.printStackTrace();
    } 
    return otherPojos; 
}).thenAcceptAsync(result -> //do something with the result); 

现在重复上述 x 次,您会注意到 try/catch 块重复了。就我而言,我有大约 15-20 个这样的电话。

有什么方法可以将上面的代码变成一两行代码吗?在supplyAsync lambda中用stop repeating myself关于exception handling

我并不是说这是唯一的方法或最有效的方法,我正在分享对我有帮助的解决方案 DRY,它可能对您有用,也可能不适合您。如果您有更好的解决方案,请分享。

我创建了以下实用方法

@Component
public class CompletableFutureUtil {
    @Autowired
    private SomeGenericServiceThatThrowsException genericService; 

    public <T> CompletableFuture<Collection<T>> fireupCompletableFutureAsynchronously(Class<T> type) {
        CompletableFuture<Collection<T>> future = CompletableFuture.supplyAsync(() -> {
            Collection<T> mystery = null;
            try {
                mystery = genericService.list(type); 
            } catch (SomeException e) {
                e.printStackTrace(); 
                //Handle the exception
            } 
            return mystery; 
        }); 
        return future; 
    }
}

现在我可以在 autowiring 之后通过以下方式重用上面的实用方法

@Autowired private CompletableFutureUtil futureUtil;

通话基本上变成一到两行 max

futureUtil.fireupCompletableFutureAsynchronously(SomePojo.class)
                .thenAcceptAsync(result -> //do something with result); 

futureUtil.fireupCompletableFutureAsynchronously(OtherPojo.class)
                .thenAcceptAsync(result -> //do something with result);

快乐DRY+ing

只需向您的 class 添加一个方法来执行所有重复的代码,并在最后一行将 Consumer<List<?>> 作为参数传递给 thenAcceptAsync

private CompletableFuture<Void> getAndAcceptAsync(Consumer<List<?>> resultProcessor) {  
    return CompletableFuture.supplyAsync(() -> {
        List<SomePojo> somePojos = null;
        try {
            somePojos = someServiceThatThrowsException.getAll(SomePojo.class);
        } catch (SomeException e) {
           //Handle the exception
           e.printStackTrace();
        } 
        return somePojos; 
    }).thenAcceptAsync(resultProcessor);
}

然后您可以根据需要多次调用它。

future1Of15 = getAndAcceptAsync(result-> { do something } );
future2Of15 = getAndAcceptAsync(result-> { do something else } );

模式 用于处理效果,例如函数式编程的失败。一种这样的模式是 Try monad,例如vavr 在 java 中提供了一个实现。

这些模式通过声明性 API 为您抽象出大量样板文件:

CompletableFuture
    .supplyAsync(() -> Try.of(() -> methodThatThrows()))
    .thenAccept(res -> System.out.println(res));

或者,如果您不必使用 CompletableFuture,您可以选择使用 Future monad 来进一步减少样板代码:

Future.of(() -> methodThatThrows())
    .onComplete(result -> System.out.println(result));

无论哪种方式,你最终得到的 Future<T> 是一个 Try<T>,它可以是 Success<T>Failure,你可以相应处理