If we have a variable for the class of a generic class, like List
, what type should it be?
List<Class> c1;
or
List<Class<?>> c2;
If we have a variable for the class of a generic class, like List
, what type should it be?
List<Class> c1;
or
List<Class<?>> c2;
The second one, because the first one uses the raw type instead of generics.
i.e. List
is raw, but List<?>
is generic, and you shouldn't mix-and-match between both raws and generics.
Do you want to represent a runtime class, or a type? (The distinction being that List<String>
and List<Integer>
are different types, but share the same runtime class).
Type: Use something like a type token.
Runtime class: Since
List<String> ls = new ArrayList<String>();
Class<? extends List> c1 = ls.getClass();
Class<List> c2 = List.class;
compiles, but
Class<? extends List<?>> c3 = ls.getClass();
Class<List<?>> c4 = List.class;
does not, I'd opt for using the raw type in the type expression. There really isn't any benefit from specifying the type argument to List
because the class does not determine it, and using a wildcard type will require weird casting to get it to the proper type, for instance:
Class<?> rawClass = List.class; // kludge: do not inline this variable, or compilation will fail
Class<List<?>> classForBadAPI = (Class<List<?>>) rawClass;
Edit: Why it doesn't compile
Lifted from the comments:
why doesn't the 2nd code compile? the code makes perfect sense. Is it a design mistake in JDK? or is there valid reason for the choice?
List.class
is of type Class<List>
. Since List<?>
and List
are different types, Class<List<?>>
and Class<List>
are unrelated types, but the right-hand type of an assignment must be a subtype of the left-hand type. The getClass()
case is analogous.
I would not blame the JDK, they only implemented the rules laid down in the language specification itself, in particular:
The type of a class literal,
C.Class
, whereC
is the name of a class, interface or array type, isClass<C>
.
(source)
The type of a method invocation
e.getClass()
, where the expressione
has the static typeT
, isClass<? extends |T|>
.
(source)
We write
|T|
for the erasure of typeT
.
(source)
... and why is it defined like that?
Compiler knows the full generic type of
e
, but why doese.getClass()
must return an erased type.
It's hard to give a definite answer to that, since the spec does not expand on the reasons for that definition. However, it might be because the runtime type might not be a subtype of the static type, a pathological situation that can arise by incorrect suppression of unchecked warnings (c.f heap pollution). By specifying that the return type only contains the erasure, the specification ensures that even in the presence of heap pollution, the class object returned by getClass()
is an instance of the declared return type of getClass()
. It also serves as a reminder that the runtime the programmer is about to access using the reflection API only thinks in terms of erased types.
Class<? extends List<?>>
since List itself is an interface here, this might be a better option.