Reputation: 36229
Say I have this method:
public CompletionStage<SomeClass> getData() {
CompletableFuture<SomeClass> future = new CompletableFuture<>();
return CompletableFuture.runAsync(() -> {
// Fetch data from some source
// Then either
// future.complete(data);
// or fail
// future.completeExceptionally(e);
});
return future;
}
And now I want to have a method, that calls getData
, manipulates, but then returns an CompletionStage<Either<ErrorResponse, Data>>
public CompletionStage<Either<ErrorResponse, Data>> modifyData() {
return getData()
.thenCompose(d -> CompletableFuture.completedFuture(Either.right(d)))
.exceptionally(e -> Either.left(ErrorResponse.create(e)));
}
When I do this, the type inside the exceptionally
is lost, and the compiler thinks I am returning a type Either<Object, Data>
.
If I however change that code to:
public CompletionStage<Either<ErrorResponse, Data>> modifyData() {
CompletableFuture<Either<ErrorResponse, Data>> future = new CompetableFuture<>();
CompletableFuture.runAsync(() -> {
getData()
.thenCompose(d -> future.complete(Either.right(d)));
.exceptionally(e ->
future.complete(Either.left(ErrorResponse.create(e)))
});
return future;
}
Then it works fine. Why is the type lost?
Upvotes: 0
Views: 1909
Reputation: 280132
You have
public CompletionStage<Either<ErrorResponse, Data>> modifyData() {
return getData()
.thenCompose(d -> CompletableFuture.completedFuture(Either.right(d)))
.exceptionally(e -> Either.left(ErrorResponse.create(e)));
}
thenCompose
is a generic method, but you don't provide enough type information for Java to infer that you want its type parameter U
to be bound to Either<ErrorResponse, Data>
. The call to exceptionally
(even if it was generic) also can't feed back type information to the invocation of thenCompose
.
Currently, it infers Object
for Either
's first type parameter. So thenCompose
returns a CompletionStage<Either<Object, Data>>
. That propagates to exceptionally
. Since CompletionStage<Either<Object, Data>>
isn't a CompletionStage<Either<ErrorResponse, Data>>
, it's an invalid type for a return value.
You can add the necessary type information by providing an explicit type argument.
public CompletionStage<Either<ErrorResponse, Data>> modifyData() {
return getData()
.<Either<ErrorResponse, Data>>thenCompose(d -> CompletableFuture.completedFuture(Either.right(d)))
.exceptionally(e -> Either.left(ErrorResponse.create(e)));
}
or closer to the problematic invocation
public CompletionStage<Either<ErrorResponse, Data>> modifyData() {
return getData()
.thenCompose(d -> CompletableFuture.completedFuture(Either.<ErrorResponse, Data>right(d)))
.exceptionally(e -> Either.left(ErrorResponse.create(e)));
}
Now Java doesn't have to try and guess. It knows what you meant.
Alternatively, I would use CompletableFuture#handle
to handle the exceptional case.
return getData()
.handle((d, e) -> {
if (e == null) {
return Either.right(d);
}
return Either.left(ErrorResponse.create(e));
});
}
The type information is self-contained. The rest is mostly a matter of opinion.
Upvotes: 1