Search code examples
javaandroidgenericsgsontype-erasure

Gson illegal type variable reference


I have a class defined as:

public class Calls<T extends Object>

Inside this class there is a method doRequest that creates a TypeToken (from com.google.json.reflect) for a Gson request.

java.lang.reflect.Type type = new TypeToken<HandlerResponse<T>>(){{}}.getType();

where HandlerResponse is a simple model class that contains attributes like:

private Map detail;

private transient T data;

private transient int count = 0;

private String status;

The exception I get on Android Studio is:

FATAL EXCEPTION: main
Process: PID: 32628
java.lang.AssertionError: illegal type variable reference
    at libcore.reflect.TypeVariableImpl.resolve(TypeVariableImpl.java:111)
    at libcore.reflect.TypeVariableImpl.getGenericDeclaration(TypeVariableImpl.java:125)
    at libcore.reflect.TypeVariableImpl.hashCode(TypeVariableImpl.java:47)
    at java.util.Arrays.hashCode(Arrays.java:4153)
    at com.google.gson.internal.$Gson$Types$ParameterizedTypeImpl.hashCode($Gson$Types.java:479)
    at com.google.gson.reflect.TypeToken.<init>(TypeToken.java:64)
    at com.company.server.Calls$4.<init>(Calls.java:244)

crashes at the instantiation of the TypeToken (I was thinking that probably is losing the T class 'cause of the type erasure of Java).

I create the instance of Calls as:

Calls<com.company.model.beans.Model> calls = new Calls<>(){};

Solution

  • Solved, I modified the implementation as follows:

    public class Calls<T> {
    
        public Calls(Type type, Class classTypeResponse) {
    
            this.type = type;
            this.classTypeResponse = classTypeResponse;
        }
    
        doRequest(...) { ... }
    
        ...
    }
    

    I have a classTypeResponse because I have a callback system that returns the right class type for the object for the request.

    I call it in this way:

    Type type = new TypeToken<HandlerResponse<com.company.model.beans.Model>>(){}.getType();
    
    Calls<com.company.model.beans.Model> calls = new Calls<>(type, com.company.model.beans.Model.class);
    
    calls.doRequest(...);
    

    The T doesn't exists at runtime, Java reflection system couldn't infer the right type for the TypeToken. The solution is to create the TypeToken without generics and pass the object where you need it.