Search code examples
javaclassgenericsrobospice

How to call the static class method for a generic object?


I need to pass the class of a generic type into a class's constructor. The class is SpiceRequest from the RoboSpice Android library for a reference to the constructor.

It seems odd that the class requires passing the generic's class into the contstructor, when it could be accessed from the generic type itself, in this case RESULT.class, but maybe I'm wrong about this. Anyway, I'm not looking to change the library's code, but rather need to use a generic type for the generic type of SpiceRequest, Map<String, ? extends Object>. Here's my code:

SpiceRequest<Map<String, ? extends Object>> request =
        new SpiceRequest<Map<String, ? extends Object>>(???) {
            ...
        };

And the signature of the SpiceRequest constructor:

public SpiceRequest(final Class<RESULT> clazz) {
    ...
}

For ??? I have tried Map.class with the compiler error: The constructor SpiceRequest<Map<String,? extends Object>>(Class<Map>) is undefined.

Map<String, ? extends Object>.class gives the error: Syntax error on tokens, PrimitiveType expected instead, specifically underlining ? extends Object. It also gives the same error as Map.class.

And Map.<String, ? extends Object>class gives the same compiler error as well.

What is the correct way to get the generic class Class<Map<String, ? extends Object>>?


Solution

  • There are no class literals for concrete parameterized types or wildcard parameterized types. From Angelika Langer's generics tutorial:

    Wildcard parameterized types lose their type arguments when they are translated to byte code in a process called type erasure. As a side effect of type erasure, all instantiations of a generic type share the same runtime representation, namely that of the corresponding raw type. In other words, parameterized types do not have type representation of their own. Consequently, there is no point to forming class literals such as List<?>.class , List<? extends Number>.class and List<Long>.class, since no such Class objects exist. Only the raw type List has a Class object that represents its runtime type. It is referred to as List.class.

    There are no class literals for concrete parameterized types for the same reasons, which in a nutshell are type erasure.

    For your purposes, you will just need to do an unchecked cast of the class literal:

    Class<Map<String, ?>> c = (Class<Map<String, ?>>)(Class<?>)Map.class;
    

    Note that the double cast through Class<?> is necessary because a direct conversion from Class<Map> to Class<Map<String, ?>> is illegal.