Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android using Retrofit with generic type

Is it possible to using retrofit in a manner that uses generic type?

for example something like this:

public interface RetroInterface<T> {
    @GET("content/{id}")
    T getById(@Path("id") int id);
}

I've read that Retrofit uses the method signature to determine the return Type at runtime, in that case is it even possible to have generic interface such as above?

like image 922
arash moeen Avatar asked Oct 27 '25 14:10

arash moeen


2 Answers

Yes I think it's possible but be carefull retrofit return some Call So you can create an interface with Call<T> like method except

But Have you really need to create a template for a service ? Because in you get annotations you ask to server one specific ressource so you known the type of response

like image 167
Romain de-coster Avatar answered Oct 29 '25 05:10

Romain de-coster


The only way to pass that info I can think of is introducing a wrapper to hold both value and its type (or type token to simplify Gson).

final class GenericBody<T> {

final T body;
final TypeToken<T> typeToken;

GenericBody(final T body, final TypeToken<T> typeToken) {
    this.body = body;
    this.typeToken = typeToken;
}

}

Then an example service might be declared as follows:

interface IGenericService {

@POST("/")
Call<Void> post(@Body @SuppressWarnings("rawtypes") GenericBody genericBody);

}

Here, the Call is declared to return nothing, and genericBody is intentionally made raw-typed to let it pass Retrofit validation.

Next, the Gson part.

final class GenericBodyTypeAdapterFactory
    implements TypeAdapterFactory {

private static final TypeAdapterFactory genericBodyTypeAdapterFactory = new GenericBodyTypeAdapterFactory();

private GenericBodyTypeAdapterFactory() {
}

static TypeAdapterFactory getGenericBodyTypeAdapterFactory() {
    return genericBodyTypeAdapterFactory;
}

@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
    if ( !GenericBody.class.isAssignableFrom(typeToken.getRawType()) ) {
        return null;
    }
    final TypeAdapter<GenericBody<T>> genericBodyTypeAdapter = new TypeAdapter<GenericBody<T>>() {
        @Override
        public void write(final JsonWriter out, final GenericBody<T> value)
                throws IOException {
            final T body = value.body;
            final TypeAdapter<T> typeAdapter = gson.getDelegateAdapter(GenericBodyTypeAdapterFactory.this, value.typeToken);
            typeAdapter.write(out, body);
        }

        @Override
        public GenericBody<T> read(final JsonReader in) {
            throw new UnsupportedOperationException();
        }
    };
    @SuppressWarnings("unchecked")
    final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) genericBodyTypeAdapter;
    return typeAdapter;
}

}

What it does it is:

checks if it can handle GenericBody instances; resolves appropriate type adapters for the by the bound type token; writes the generic body value to the output. No read is implemented.

Example of use (full of mocks (staticResponse(applicationJsonMediaType, "OK")) that can be easily translated to your code):

private static final TypeToken<List<String>> stringListTypeToken = new 
TypeToken<List<String>>() {
};

 private static final Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(getGenericBodyTypeAdapterFactory())
    .create();

private static final OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(staticResponse(applicationJsonMediaType, "OK"))
    .build();

 private static final Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://whatever")
    .client(client)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build();

 private static final IGenericService genericService = 
 retrofit.create(IGenericService.class);

 public static void main(final String... args)
    throws IOException {
final GenericBody<List<String>> body = new GenericBody<>(asList("foo", "bar", "baz"), 
stringListTypeToken);
genericService.post(body).execute();
}

This would write ["foo","bar","baz"] to the output stream respecting properly configured Gson (de)serialization strategies.

like image 28
Abdul Ahad Avatar answered Oct 29 '25 05:10

Abdul Ahad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!