I used to have a callable class
class SampleTask implements Callable<Double> {
@Override
public Double call() throws Exception {
return 0d;
}
}
I used to use ExecutorService
to submit the Callable
. How to change to use CompletableFuture.supplyAsync
?
The following code cannot compile
SampleTask task = new SampleTask();
CompletableFuture.supplyAsync(task);
No instance of type of variable U exists so that SampleTask conforms to Supplier
For your callable as written, you could simply use CompletableFuture.supplyAsync(() -> 0d);
.
If, however, you have an existing Callable
, using it with CompletableFuture
is not so straight-forward due to the checked exceptions that a callable might throw.
You may use an ad-hoc Supplier
which catches exceptions and re-throws it wrapped in an unchecked exception like
CompletableFuture.supplyAsync(() -> {
try { return callable.call(); }
catch(Exception e) { throw new CompletionException(e); }
})
Using the specific type CompletionException
instead of an arbitrary subtype of RuntimeException
avoids getting a CompletionException
wrapping a runtime exception wrapping the actual exception when calling join()
.
Still, you'll notice the wrapping when chaining an exception handler to the CompletableFuture
. Also, the CompletionException
thrown by join()
will be the one created in the catch
clause, hence contain the stack trace of some background thread rather than the thread calling join()
. In other words, the behavior still differs from a Supplier
that throws an exception.
Using the slightly more complicated
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;
}
you get a CompletableFuture
which behaves exactly like supplyAsync
, without additional wrapper exception types, ie if you use
callAsync(task).exceptionally(t -> {
t.printStackTrace();
return 42.0;
})
t
will be the exact exception thrown by the Callable
, if any, even if it is a checked exception. Also callAsync(task).join()
would produce a CompletionException
with a stack trace of the caller of join()
directly wrapping the exception thrown by the Callable
in the exceptional case, exactly like with the Supplier
or like with runAsync
.
supplyAsync()
expects a Supplier<U>
and you are giving it a Callable
.
The error message is telling you that the compiler has tried to find a type to use for U
such that your SampleTask
"is a" Supplier<U>
, but it can't find one.
Java will implicitly "promote" a lambda to a functional interface such as Callable
or Supplier
. But it won't treat functional interfaces as interchangeable -- that is, you can't use a Callable
where a Supplier
is expected.
You can make a suitable lambda in-place:
SimpleTask task = new SimpleTask();
CompletableFuture.supplyAsync(() -> task.call());
Note that this works if SimpleTask
's call()
is:
public Double call() { // note no exception declared
return 0d;
}
The fact that SimpleTask
happens to implement Callable
is not relevant to the code above.
If you want this to work with an arbitrary Callable
, or if you declare task
as a Callable
:
Callable callable = new SimpleTask();
CompletableFuture.supplyAsync(() -> callable.call());
... then you will get a compiler error about the uncaught exception. Your lambda will need to catch the exception and handle it (perhaps rethrowing as an unchecked exception, as described in other answers).
Or you could make SampleTask
implement Supplier<Double>
.
Part of the motivation for lambdas is that writing things like Callable
was too verbose. So you might well leave out the intermediate class and go directly for:
CompleteableFuture<Double> future = CompletableFuture.supplyAsync(() -> 0d);
This applies for more complicated suppliers too:
CompleteableFuture<Double> future = CompletableFuture.supplyAsync(() -> {
Foo foo = slowQuery();
return transformToDouble(foo);
});
Since CompleteableFuture::supplyAsync
expects a Supplier<Double>
and not Callable<Double>
you should go with:
Callable<Double> task = new SampleTask();
CompletableFuture.supplyAsync(() -> {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
I had this come up recently and used Vavr to solve it (was already using it for other things, too), and it worked out great for me:
CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
Or to get a Supplier of that CompletableFuture:
() -> CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
In all cases I tested this returned exactly and threw exactly what the callable itself did.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.