Claud
Claud

Reputation: 1075

Inheritance with Retrofit failed to invoke, with no args

I have this base abstract class that looks like this.

public abstract class Species implements Parcelable {
    public Species() { 

    }

    public abstract String name();
}

And then my Human class looks like this.

@AutoValue
public abstract class Human extends Species implements Parcelable {
    public static Human create(String humanVariable) {
        return new AutoValue_Human(humanVariable);
    }

    public static Human create(String name, String humanVariable) {
        return new AutoValue_Human(name, humanVariable);
    }

    public static TypeAdapter<Human> typeAdapter(Gson gson) {
        return new AutoValue_Human.GsonTypeAdapter(gson);
    }

    @SerializedName("name")
    public abstract String name();

    @Nullable
    @SerializedName("human_variable")
    public abstract String humanVariable();

}

E/com.service.androidbrain.util.networking.RetrofitCallback.onFailure: Failed to invoke public com.service.example.models.Species() with no args

For some reason however I'm getting this error and I don't understand what is going on, any idea?

Upvotes: 1

Views: 2201

Answers (4)

Hector Ortiz
Hector Ortiz

Reputation: 1

I solved this problem by removing from my class the word abstract

Ej Before

    public abstract class Post {
    
    private int userId;
    private int id;
    private String title;
}

after

public class Post {
    
    private int userId;
    private int id;
    private String title;
} 

Upvotes: -1

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21105

I'm sure you just don't have the GsonConverterFactory instantiated correctly. From my answer to your previous question:

@GsonTypeAdapterFactory
abstract class HumanAdapterFactory
        implements TypeAdapterFactory {

    public static TypeAdapterFactory create() {
        return new AutoValueGson_HumanAdapterFactory();
    }

}

Therefore the following is unnecessary:

public static TypeAdapter<Human> typeAdapter(Gson gson) {
    return new AutoValue_Human.GsonTypeAdapter(gson);
}

So you just have to instantiate Gson:

new GsonBuilder()
        ...
        .registerTypeAdapterFactory(HumanAdapterFactory.create())
        .create();

And configure the Retrofit instance like this:

new Retrofit.Builder()
        ...
        .addConverterFactory(GsonConverterFactory.create(gson)) // THIS is necessary
        .build();

And make sure your service interface operates on Human, not Species:

interface IService {

    @GET("/") // This is probably what you really want
    Call<Human> getHuman();

    @GET("/") // How can you know WHAT the Species is here?
    Call<Species> getSpecies();

}

If you really want to go with getSpecies(), you MUST know what is the real type of the Species interface for a particular object: so you either have to use InstanceCreator or detect the real type on some information. Both of the approaches are described in my answer. In order to make it work:

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(HumanAdapterFactory.create())
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                // ... that big type adapter here from that answer OR InstanceCreator by it's not useful here
            }
        })
        .create();

final Retrofit retrofit = new Retrofit.Builder()
        ...
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();

final IService service = retrofit.create(IService.class);
final Species species = service.getSpecies()
        .execute()
        .body();
System.out.println(species.getClass());
System.out.println(species.name());

That's all you need, but Call<Human> getHuman(); is the best choice here.

Upvotes: 2

tynn
tynn

Reputation: 39843

auto-value-gson doesn't support the behavior you're trying to achieve.

I assume you declared your retrofit service to return Call<Species>, while only Human.typeAdapter(Gson) is registered to Gson. This concludes in Gson not knowing of how to acutally create an instance of Species.

To make this work, you'd have to (create and) install another type adapter for Species which knows how to identify the actual subtype of the species and delegates all model creation to the specific type adapter.

Upvotes: 2

azizbekian
azizbekian

Reputation: 62189

You should prefer composition over inheritance, because at least currently it is not available to do that in AutoValue.

See this issue at github.

Upvotes: 0

Related Questions