The project that I work on have some code that can be simplified as the following two functions:
CompletionStage<Collection<String>> fn1(String prefix) {
return fn2()
.thenApply(list -> list.stream()
.map(s -> prefix + s)
.collect(Collectors.toList())
);
}
CompletionStage<List<String>> fn2() {
return CompletableFuture.completedFuture(List.of("results", "of", "computation"));
}
It works well. However, fn1
cannot compile after adding an exceptionally
step:
CompletionStage<Collection<String>> fn1(String prefix) {
return fn2()
.thenApply(list -> list.stream()
.map(s -> prefix + s)
.collect(Collectors.toList())
).exceptionally(t -> {
// Log the error
// ...
// return an empty collection
Collection<String> emptyCol = List.of();
return emptyCol; // Error: Collection<String> cannot be converted to List<String>
// Also tried the following two ways
// List<String> emptyList = List.of();
// return emptyList; // Error: CompletionStage<List<String>> cannot be converted to CompletionStage<Collection<String>>
// return emptyList.stream().collect(Collectors.toList()); // Error same as above
});
}
How to fix it, supposing that we cannot change the signature of fn1
? Why .collect(Collectors.toList())
is fine for thenApply
?
BTW, the JDK is 11.
exceptionally
must return the same type as the original future. Since the generic type of your return type is Collection<String>
, you must help the Java compiler figure out that your future really returns a Collection
and not a List
.
All options to fix this issue are trivial: type witness, cast, or temporary variable.
Type witness:
CompletionStage<Collection<String>> fn1(String prefix) {
return fn2()
.<Collection<String>>thenApply(list -> list.stream()
.map(s -> prefix + s)
.collect(Collectors.toList())
).exceptionally(t -> Collections.emptyList());
}
Cast:
CompletionStage<Collection<String>> fn1(String prefix) {
return fn2()
.thenApply(list -> (Collection<String>)list.stream()
.map(s -> prefix + s)
.collect(Collectors.toList())
).exceptionally(t -> Collections.emptyList());
}
Variable:
CompletionStage<Collection<String>> fn1(String prefix) {
final CompletionStage<Collection<String>> stage = fn2()
.thenApply(list -> list.stream()
.map(s -> prefix + s)
.collect(Collectors.toList()));
return stage.exceptionally(t -> Collections.emptyList());
}