如何转换代码以使用CompletableFuture?
我以前有一个可调用的类
class SampleTask implements Callable { @Override public Double call() throws Exception { return 0d; } }
我曾经使用ExecutorService
来提交Callable
。 如何更改为使用CompletableFuture.supplyAsync
?
以下代码无法编译
SampleTask task = new SampleTask(); CompletableFuture.supplyAsync(task);
不存在变量类型U的实例,以便SampleTask符合供应商
supplyAsync()
期望Supplier
并且您给它一个Callable
。
错误消息告诉您编译器已尝试查找要用于U
的类型,以使您的SampleTask
“是” Supplier
,但找不到。
Java将隐式地将lambda“推广”到function接口,例如Callable
或Supplier
。 但它不会将function接口视为可互换的 – 也就是说,您不能在需要Supplier
地方使用Callable
。
你可以在适当的位置制作一个合适的lambda:
SimpleTask task = new SimpleTask(); CompletableFuture.supplyAsync(() -> task.call());
请注意,如果SimpleTask
的call()
是:
public Double call() { // note no exception declared return 0d; }
SimpleTask
恰好实现Callable
的事实与上面的代码无关。
如果您希望这可以使用任意Callable
,或者您将task
声明为Callable
:
Callable callable = new SimpleTask(); CompletableFuture.supplyAsync(() -> callable.call());
…然后,您将收到有关未捕获exception的编译器错误。 您的lambda将需要捕获exception并处理它(可能将其重新抛出为未经检查的exception,如其他答案中所述)。
或者您可以使SampleTask
实现Supplier
。
lambdas的部分动机是写像Callable
这样的东西太冗长了。 所以你可能会遗漏中间类并直接进入:
CompleteableFuture future = CompletableFuture.supplyAsync(() -> 0d);
这也适用于更复杂的供应商:
CompleteableFuture future = CompletableFuture.supplyAsync(() -> { Foo foo = slowQuery(); return transformToDouble(foo); });
对于你所编写的callable,你可以简单地使用CompletableFuture.supplyAsync(() -> 0d);
。
但是,如果你有一个现有的Callable
,那么将它与CompletableFuture
一起使用并不是那么简单,因为可调用的exception可能会抛出。
您可以使用ad-hoc Supplier
捕获exception并重新抛出exception,例如,包含在未经检查的exception中
CompletableFuture.supplyAsync(() -> { try { return callable.call(); } catch(Exception e) { throw new CompletionException(e); } })
使用特定类型CompletionException
而不是RuntimeException
的任意子类型可以避免在调用join()
时包含一个包含实际exception的运行时exception的CompletionException
。
但是,当您将exception处理程序链接到CompletableFuture
时,您会注意到包装。 此外, join()
抛出的CompletionException
将是catch
子句中创建的CompletionException
,因此包含一些后台线程的堆栈跟踪,而不是调用join()
的线程。 换句话说,行为仍然不同于抛出exception的Supplier
。
使用稍微复杂一些
public static CompletableFuture callAsync(Callable callable) { CompletableFuture cf = new CompletableFuture<>(); CompletableFuture.runAsync(() -> { try { cf.complete(callable.call()); } catch(Throwable ex) { cf.completeExceptionally(ex); } }); return cf; }
你得到一个CompletableFuture
,其行为与supplyAsync
完全相同,没有额外的包装器exception类型,即如果你使用的话
callAsync(task).exceptionally(t -> { t.printStackTrace(); return 42.0; })
t
将是Callable
抛出的确切exception(如果有的话),即使它是一个已检查的exception。 callAsync(task).join()
也会产生一个CompletionException
,其中包含join()
调用者的堆栈跟踪,直接包装Callable
在exception情况下抛出的exception,就像与Supplier
或runAsync
。
由于CompleteableFuture::supplyAsync
需要Supplier
而不能Callable
您应该使用:
Callable task = new SampleTask(); CompletableFuture.supplyAsync(() -> { try { return task.call(); } catch (Exception e) { throw new RuntimeException(e); } });