Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
121 views
in Technique[技术] by (71.8m points)

java - How to convert the code to use CompletableFuture?

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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, i.e. 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...