使用 CompletableFuture 抛出已检查的异常
Throwing checked exceptions with CompletableFuture
Whosebug 包含多个关于混合检查异常与 CompletableFuture
的问题。
这里有几个例子:
- Checked exception with CompletableFuture
- Java 8 Supplier Exception handling
- JDK8 CompletableFuture.supplyAsync how to deal with interruptedException
虽然一些答案暗示使用 CompletableFuture.completeExceptionally()
他们的方法导致用户代码难以阅读。
我将使用此 space 提供替代解决方案,从而提高可读性。
请注意,此问题特定于 CompletableFuture。这使我们能够提供更普遍的不扩展到 lambda 表达式的解决方案。
鉴于 Completions
实用程序 class(在下面提供)用户可以无缝地抛出已检查的异常:
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
实用程序 class 中的一些方法。您可以用这种方式包装其他 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()
{}
}
Whosebug 包含多个关于混合检查异常与 CompletableFuture
的问题。
这里有几个例子:
- Checked exception with CompletableFuture
- Java 8 Supplier Exception handling
- JDK8 CompletableFuture.supplyAsync how to deal with interruptedException
虽然一些答案暗示使用 CompletableFuture.completeExceptionally()
他们的方法导致用户代码难以阅读。
我将使用此 space 提供替代解决方案,从而提高可读性。
请注意,此问题特定于 CompletableFuture。这使我们能够提供更普遍的不扩展到 lambda 表达式的解决方案。
鉴于 Completions
实用程序 class(在下面提供)用户可以无缝地抛出已检查的异常:
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
实用程序 class 中的一些方法。您可以用这种方式包装其他 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()
{}
}