使用 CompletableFuture 抛出已检查的异常

问题描述 投票:0回答:2

Stackoverflow 包含多个有关将已检查异常与

CompletableFuture
混合的问题。

以下是一些示例:

虽然一些答案暗示使用

CompletableFuture.completeExceptionally()
,但他们的方法会导致用户代码难以阅读。

我将利用这个空间提供替代解决方案,以提高可读性。

请注意,这个问题特定于 CompletableFuture。 这使我们能够提供不更普遍地扩展到 lambda 表达式的解决方案。

java completable-future
2个回答
2
投票

鉴于

Completions
实用程序类(下面提供),用户可以无缝抛出已检查的异常:

public CompletionStage<String> readLine()
{
  return Completions.supplyAsync(() ->
  {
    try (BufferedReader br = new BufferedReader(new FileReader("test.txt")))
    {
      return br.readLine();
    }
  });
}

lambda 抛出的任何异常(检查或未检查)都将包装在

CompletionException
中,这与
CompletableFuture
对于未检查异常的行为一致。

对于像

thenApply()
这样的中间步骤,事情会变得有点难看,但这并不是世界末日:

public CompletionStage<String> transformLine()
{
  return readLine().thenApply(line ->
    Completions.wrapExceptions(() ->
    {
      if (line.contains("%"))
        throw new IOException("Lines may not contain '%': " + line);
      return "transformed: " + line;
    }));
}

这里有一些来自

Completions
实用程序类的方法。您可以通过这种方式包装其他
CompletableFuture
方法。

/**
 * Helper functions for {@code CompletionStage}.
 *
 * @author Gili Tzabari
 */
public final class Completions
{
    /**
     * Returns a {@code CompletionStage} that is completed with the value or exception of the {@code CompletionStage}
     * returned by {@code callable} using the supplied {@code executor}. If {@code callable} throws an exception the
     * returned {@code CompletionStage} is completed with it.
     *
     * @param <T>      the type of value returned by {@code callable}
     * @param callable returns a value
     * @param executor the executor that will run {@code callable}
     * @return the value returned by {@code callable}
     */
    public static <T> CompletionStage<T> supplyAsync(Callable<T> callable, Executor executor)
    {
        return CompletableFuture.supplyAsync(() -> wrapExceptions(callable), executor);
    }

    /**
     * Wraps or replaces exceptions thrown by an operation with {@code CompletionException}.
     * <p>
     * If the exception is designed to wrap other exceptions, such as {@code ExecutionException}, its underlying cause is wrapped; otherwise the
     * top-level exception is wrapped.
     *
     * @param <T>      the type of value returned by the callable
     * @param callable an operation that returns a value
     * @return the value returned by the callable
     * @throws CompletionException if the callable throws any exceptions
     */
    public static <T> T wrapExceptions(Callable<T> callable)
    {
        try
        {
            return callable.call();
        }
        catch (CompletionException e)
        {
            // Avoid wrapping
            throw e;
        }
        catch (ExecutionException e)
        {
            throw new CompletionException(e.getCause());
        }
        catch (Throwable e)
        {
            throw new CompletionException(e);
        }
    }

    /**
     * Returns a {@code CompletionStage} that is completed with the value or exception of the {@code CompletionStage}
     * returned by {@code callable} using the default executor. If {@code callable} throws an exception the returned
     * {@code CompletionStage} is completed with it.
     *
     * @param <T>      the type of value returned by the {@code callable}
     * @param callable returns a value
     * @return the value returned by {@code callable}
     */
    public static <T> CompletionStage<T> supplyAsync(Callable<T> callable)
    {
        return CompletableFuture.supplyAsync(() -> wrapExceptions(callable));
    }

    /**
     * Prevent construction.
     */
    private Completions()
    {}
}

0
投票
  1. 声明辅助类
public class CompletableFutureHelper {
    public static <T> CompletableFuture<T> supplyExceptionAsync(SupplierException<T> supplierException) {
        return supplyExceptionAsync(supplierException, ForkJoinPool.commonPool());
    }

    public static <T> CompletableFuture<T> supplyExceptionAsync(SupplierException<T> supplierException, Executor executor) {
        final CompletableFuture<T> completableFuture = new CompletableFuture<>();
        executor.execute(() -> {
            try {
                completableFuture.complete(supplierException.get());
            } catch (Throwable e) {
                completableFuture.completeExceptionally(e);
            }
        });
        return completableFuture;
    }

    public static CompletableFuture<?> runExceptionAsync(RunnableException runnableException) {
        return runExceptionAsync(runnableException, ForkJoinPool.commonPool());
    }

    public static CompletableFuture<?> runExceptionAsync(RunnableException runnableException, Executor executor) {
        final CompletableFuture<?> completableFuture = new CompletableFuture<>();
        executor.execute(() -> {
            try {
                runnableException.run();
                completableFuture.complete(null);
            } catch (Throwable e) {
                completableFuture.completeExceptionally(e);
            }
        });
        return completableFuture;
    }

    public interface RunnableException {
        void run() throws Exception;
    }

    public interface SupplierException<T> {
        T get() throws Exception;
    }
}
  1. 像这样使用它
        try {
            CompletableFutureHelper.runExceptionAsync(() -> Thread.sleep(1000)).get();
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }



        try {
            CompletableFutureHelper.supplyExceptionAsync(() -> Files.readAllBytes(new File("").toPath())).get();
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
© www.soinside.com 2019 - 2024. All rights reserved.