我曾经有一个可调用的类
class SampleTask implements Callable<Double> {
@Override
public Double call() throws Exception {
return 0d;
}
}
我曾使用ExecutorService
提交Callable
。如何更改为使用CompletableFuture.supplyAsync
?
以下代码无法编译
SampleTask task = new SampleTask();
CompletableFuture.supplyAsync(task);
不存在变量类型U的实例,因此SampleTask符合供应商
答案 0 :(得分:6)
对于您编写的可调用内容,您只需使用CompletableFuture.supplyAsync(() -> 0d);
。
但是,如果您有一个Callable
,那么将其与CompletableFuture
一起使用并不是那么简单,因为可调用的异常可能会抛出。
你可以使用一个特殊的Supplier
来捕获异常并重新抛出它包含在未经检查的异常中,如
CompletableFuture.supplyAsync(() -> {
try { return callable.call(); }
catch(Exception e) { throw new CompletionException(e); }
})
使用特定类型CompletionException
而不是RuntimeException
的任意子类型,可以避免在调用CompletionException
时包装一个包含实际异常的join()
运行时异常。
但是,当您将异常处理程序链接到CompletableFuture
时,您会注意到包装。此外,CompletionException
抛出的join()
将是catch
子句中创建的join()
,因此包含一些后台线程的堆栈跟踪,而不是调用Supplier
的线程。换句话说,行为仍然不同于抛出异常的public static <R> CompletableFuture<R> callAsync(Callable<R> callable) {
CompletableFuture<R> cf = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try { cf.complete(callable.call()); }
catch(Throwable ex) { cf.completeExceptionally(ex); }
});
return cf;
}
。
使用稍微复杂的
CompletableFuture
你得到supplyAsync
,其行为与callAsync(task).exceptionally(t -> {
t.printStackTrace();
return 42.0;
})
完全相同,没有其他包装器异常类型,即使用
t
Callable
将是callAsync(task).join()
抛出的确切异常(如果有),即使它是已检查的异常。此外,CompletionException
将生成join()
,其中Callable
调用者的堆栈跟踪直接包装Supplier
在异常情况下抛出的异常,与{runAsync
完全相同1}}或类似于expand: function(field, opts) {
field.picker.activeDate = new Date();
field.picker.update(null, false);
}
。
答案 1 :(得分:3)
supplyAsync()
预计会Supplier<U>
而你正在给它Callable
。
错误消息告诉您编译器已尝试查找要用于U
的类型,以使您的SampleTask
“为”Supplier<U>
,但找不到之一。
Java会隐式地将lambda“推广”到功能界面,例如Callable
或Supplier
。但它不会将功能接口视为可互换的 - 也就是说,您不能使用期望Callable
的{{1}}。
你可以在适当的位置制作合适的lambda:
Supplier
请注意,如果SimpleTask task = new SimpleTask();
CompletableFuture.supplyAsync(() -> task.call());
的{{1}}为:
SimpleTask
call()
恰好实施 public Double call() { // note no exception declared
return 0d;
}
的事实与上述代码无关。
如果您希望将此工作与任意SimpleTask
一起使用,或者将Callable
声明为Callable
:
task
...然后您将收到有关未捕获异常的编译器错误。你的lambda将需要捕获异常并处理它(可能重新抛出为未经检查的异常,如其他答案中所述)。
或者您可以Callable
实施Callable callable = new SimpleTask();
CompletableFuture.supplyAsync(() -> callable.call());
。
lambdas的部分动机是写SampleTask
这样的东西太冗长了。所以你可能会遗漏中间类并直接进入:
Supplier<Double>
这也适用于更复杂的供应商:
Callable
答案 2 :(得分:1)
由于CompleteableFuture::supplyAsync
期望Supplier<Double>
而不是Callable<Double>
,您应该选择:
Callable<Double> task = new SampleTask();
CompletableFuture.supplyAsync(() -> {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
答案 3 :(得分:0)
我最近遇到了这个问题,并使用Vavr来解决它(也已经将它用于其他用途),对我来说非常有用:
CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
或获取该CompletableFuture的供应商:
() -> CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
在所有情况下,我都测试了此结果是否完全返回并完全抛出了可调用对象本身的操作。