Java 使用CompletableFuture抛出已检查的异常

Java 使用CompletableFuture抛出已检查的异常,java,completable-future,Java,Completable Future,Stackoverflow包含多个关于将选中的异常与CompletableFuture混合的问题 以下是几个例子: 虽然一些答案暗示使用了CompletableFuture.completeeexceptionaly(),但他们的方法会导致用户代码难以阅读 我将使用此空间提供另一种解决方案,以提高可读性 请注意,此问题特定于CompletableFuture。这使我们能够提供不扩展到更一般的lambda表达式的解决方案。鉴于Completions实用程序类(如下提供),用户可以无缝抛出

Stackoverflow包含多个关于将选中的异常与
CompletableFuture
混合的问题

以下是几个例子:

虽然一些答案暗示使用了
CompletableFuture.completeeexceptionaly()
,但他们的方法会导致用户代码难以阅读

我将使用此空间提供另一种解决方案,以提高可读性


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

鉴于
Completions
实用程序类(如下提供),用户可以无缝抛出选中的异常:

public CompletionStage<String> readLine()
{
  return Completions.supplyAsync(() ->
  {
    try (BufferedReader br = new BufferedReader(new FileReader("test.txt")))
    {
      return br.readLine();
    }
  });
}
下面是
完成
实用程序类中的一些方法。您可以用这种方式包装其他
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()
    {}
}
/**
*{@code CompletionStage}的助手函数。
*
*@作者Gili Tzabari
*/
公开期末结业
{
/**
*返回一个{@code CompletionStage},该值以{@code CompletionStage}的值或异常完成
*由{@code callable}使用提供的{@code executor}返回。如果{@code callable}引发异常
*返回的{@code CompletionStage}已使用它完成。
*
*@param{@code callable}返回的值的类型
*@param callable返回一个值
*@param executor将运行{@code callable}的执行器
*@return由{@code callable}返回的值
*/
公共静态CompletionStage SupplySync(可调用、可调用、执行器-执行器)
{
返回CompletableFuture.SupplySync(()->wrapExceptions(可调用),executor);
}
/**
*用{@code CompletionException}包装或替换操作引发的异常。
*
*如果异常被设计为包装其他异常,例如{@code ExecutionException},则其根本原因被包装;否则
*顶级异常已包装。
*
*@param可调用函数返回的值的类型
*@param可调用返回值的操作
*@return可调用函数返回的值
*@throws CompletionException如果可调用的抛出任何异常
*/
公共静态T WrapeExceptions(可调用可调用)
{
尝试
{
返回callable.call();
}
捕获(完成异常e)
{
//避免包装
投掷e;
}
捕获(执行例外)
{
抛出新的CompletionException(例如getCause());
}
捕获(可丢弃的e)
{
抛出新的CompletionException(e);
}
}
/**
*返回一个{@code CompletionStage},该值以{@code CompletionStage}的值或异常完成
*{@code callable}使用默认执行器返回。如果{@code callable}引发异常,则返回
*{@code CompletionStage}已使用它完成。
*
*@param{@code callable}返回的值的类型
*@param callable返回一个值
*@return由{@code callable}返回的值
*/
公共静态CompletionStage SupplySync(可调用可调用)
{
返回CompletableFuture.SupplySync(()->wrapExceptions(可调用));
}
/**
*防止施工。
*/
私人完成()
{}
}

我不确定捕获所有可丢弃的
类型并包装在
CompletionException
中是否是一个好主意。特别是,我认为这对
java.lang.Error
@msandiford尤其不利,如果深入研究
CompletableFuture
实现,您将看到它们也会这样做(例如在
uniApply()
方法中)。用户希望能够记录错误,比如
AssertionError
,否则他们的代码会自动失败,他们不知道出了什么问题。也许我没有抓住你想说的要点,但是
CompletableFuture
机器能处理这种情况吗?在什么情况下,
AssertionError
(例如)会被吞没而不会在管道中的某个地方导致用户可见的
ExecutionException
。@msandiford
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()
    {}
}