So, after some digging into the abysm of reflexion I discovered that you can implement ParameterizedType
(since it's an interface) and use that to generate a different call from retrofit.
Inside my CallAdapter.Factory
implementation I created this type:
private static class AwesomeResponseType implements ParameterizedType {
private final Type innerType;
public BrainResponseType(Type innerType) {
this.innerType = innerType;
}
@Override
public Type[] getActualTypeArguments() {
return new Type[] {innerType};
}
@Override
public Type getRawType() {
return AwesomeResponse.class;
}
@Override
public Type getOwnerType() {
return null;
}
}
and when creating the adapter I inject the inner type like this:
final Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);
ParameterizedType parameterizedType = new AwesomeResponseType(innerType);
and used the parameterizedType
to create a custom CallAdapter
like this:
private static class AwesomeCallAdapter<T> implements CallAdapter<AwesomeCall<?>> {
private final Type responseType;
private AwesomeCallAdapter(Type responseType) {
this.responseType = responseType;
}
@Override
public Type responseType() {
return responseType;
}
@SuppressWarnings("unchecked")
@Override
public <R> AwesomeCall<T> adapt(Call<R> call) {
return new AwesomeCall<>((Call<AwesomeResponse<T>>)call);
}
}
The AwesomeCall
can now happily process a call that expects an AwesomeResponse:
public class AwesomeCall<T> {
private final Call<AwesomeResponse<T>> call;
public AwesomeCall(Call<AwesomeResponse<T>> call) {
this.call = call;
}
public AwesomeResponse<T> execute() throws IOException {
Response<BrainResponse<T>> response = call.execute();
return response.isSuccessful() ? response.body() : errorResponseFrom(response);
}
private AwesomeResponse<T> errorResponseFrom(Response<AwesomeResponse<T>> response) {
return new AwesomeResponse<>(null, new AwesomeError(response.message()));
}
}
If you are using Guava, I believe you don't have to implement ParameterizedType
. TypeToken
has an API to do this:
TypeToken.of(AwesomeResponse.class).where(new TypeParameter<T>() {}, innerType).getType();
But sadly, the TypeToken
in Gson doesn't support that functionality :(