Java :: obtaining parameterized type names at runt

2019-09-15 02:28发布

问题:

The following code:

public static void main(String args[]) throws NoSuchFieldException {
    List<Integer> li = new ArrayList<Integer>();
    ParameterizedType apType = (ParameterizedType) li.getClass().getGenericSuperclass();
    Type[] typeArguments = apType.getActualTypeArguments();
    int _i = 0;
    for (Type type : typeArguments) {
        System.out.format("parameterized type %d is: %s", ++_i, type);
    }
}

produces:

parameterized type 1 is: E
  1. Do I understand correctly that this is because of the effects of Type Erasure and that it should be interpreted as "unknown", given that E is the type parameter name?
  2. It strikes me as odd that I somehow have to interpret the name "E" (and I suppose "S", "U", etc. if more exist) specially. What if I have a class "E"? Moreover the method name is get_Actual_TypeArguments. Given that type parameters serve as placeholders much like variables I find it very odd that a method should return the name of the variable. Am I missing something here?
  3. Also, it's not clear to me why I can cast the superclass to ParameterizedType but not the class itself. Trying to cast the result of li.getClass() as a ParameterizedType yields the following compile time error:

    required: ParameterizedType found: Class where CAP#1 is a fresh type-variable: CAP#1 extends List from capture of ? extends List

I've seen this relevant SO post but it hasn't enlightened me.

回答1:

All instances of a generic class share the same runtime class:

new ArrayList<Integer>().getClass() == new ArrayList<String>().getClass()

Put differently, the runtime does not track the actual type arguments used to instantiate a generic class. However, it does know the types declared in the source code, and that's what getGenericSuperclass() and friends return. So if you have a class:

class Environment extends HashMap<String, Object> {

}

The expression

new Environment().getClass().getGenericSuperclass()

returns

java.util.HashMap<java.lang.String, java.lang.Object>

In contrast, if you declare

class Environment<E> extends HashMap<String, E> {
}

the same expression returns

java.util.HashMap<java.lang.String, E>

So yes, getGenericSuperclass returns the actual type parameters as declared in the source code, but those type parameters may contain type variables declared elsewhere.

Also, it's not clear to me why I can cast the superclass to ParameterizedType but not the class itself.

A ParametrizedType object represents a compile time type with a non-empty list of type arguments, a Class object a runtime type (which doesn't have any type arguments). Therefore, a Class is not a ParametrizedType.