Search code examples
javareflectionarraylistgson

Gson TypeToken with dynamic ArrayList item type


I have this code:

Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);

It converts a JSON string to a List of objects. But now I want to have this ArrayList with a dynamic type (not just myClass), defined at runtime.

The ArrayList's item type will be defined with reflection.

I tried this:

    private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
        Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
        return typeOfObjectsListNew;
    }

But it doesn't work. This is the exception:

java.sql.SQLException: Fail to convert to internal representation: {....my json....}

Solution

  • The syntax you are proposing is invalid. The following

    new TypeToken<ArrayList<Class.forName(MyClass)>>
    

    is invalid because you're trying to pass a method invocation where a type name is expected.

    The following

    new TypeToken<ArrayList<T>>() 
    

    is not possible because of how generics (type erasure) and reflection works. The whole TypeToken hack works because Class#getGenericSuperclass() does the following

    Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.

    If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.

    In other words, if it sees ArrayList<T>, that's the ParameterizedType it will return and you won't be able to extract the compile time value that the type variable T would have had.

    Type and ParameterizedType are both interfaces. You can provide an instance of your own implementation (define a class that implements either interface and overrides its methods) or use one of the helpful factory methods that TypeToken provides in its latest versions. For example,

    private Type setModelAndGetCorrespondingList2(Class<?> typeArgument) {
        return TypeToken.getParameterized(ArrayList.class, typeArgument).getType();
    }