Search code examples
classgenericstypesreflectionfastjson

Why Class.java don't provide methods like `Type getType()` and `Type getGenericType()`?


Why I have to define a subclass to get the Type of superclass' generic param? Is the limit necessary?

I read the code of Fastjson of Alibaba and tried to figure out why use TypeReference must create an anonymous subclass. Then I found that an object cannot get its own generic param Type even its own Type.

public class TypeReference {
    static ConcurrentMap<Type, Type> classTypeCache
            = new ConcurrentHashMap<Type, Type>(16, 0.75f, 1);

    protected final Type type;

    protected TypeReference() {
        Type superClass = getClass().getGenericSuperclass();

        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

        Type cachedType = classTypeCache.get(type);
        if (cachedType == null) {
            classTypeCache.putIfAbsent(type, type);
            cachedType = classTypeCache.get(type);
        }

        this.type = cachedType;
    }
    // ...
}

Sorry for my poor English. Thanks for your answers.


Solution

  • Because of Type Erasure.

    Consider the following example

    List<String> stringList = new ArrayList<>();
    List<Number> numberList = new ArrayList<>();
    
    System.out.println(stringList.getClass() == numberList.getClass());
    

    This will print true. Regardless of the generic type, both instances of ArrayList have the same class and a single Class object. So how could this single Class object return the right Type for both objects?

    We can even get a step further,

    List<String> stringList = Collections.emptyList();
    List<Number> numberList = Collections.emptyList();
    
    System.out.println(stringList == (Object)numberList);
    

    Objects do not know their generic type. If a collection is immutable and always empty, it can be used to represent arbitrary empty lists. The same applies to stateless functions

    Function<String, String> stringFunction = Function.identity();
    Function<Number, Number> numberFunction = Function.identity();
    
    System.out.println(stringFunction == (Object)numberFunction);
    

    Prints true (on most systems; this is not a guaranteed behavior).

    Generic types are only retained in some specific cases, like the signatures of field and method declarations and generic super types.

    That’s why you need to create a subclass to exploit the fact that it will store the declared generic supertype. While it sometimes would be useful to construct a Type instance in a simpler way and a suitable factory method can be regarded a missing feature, getting the actual generic type of an arbitrary object (or its Class) is not possible in general.