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
.