Class or Class [closed]

2019-10-03 01:11发布

问题:

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;  

回答1:

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.



回答2:

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, where C is the name of a class, interface or array type, is Class<C>.

(source)

The type of a method invocation e.getClass(), where the expression e has the static type T, is Class<? extends |T|>.

(source)

We write |T| for the erasure of type T.

(source)

... and why is it defined like that?

Compiler knows the full generic type of e, but why does e.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.



回答3:

Class<? extends List<?>>

since List itself is an interface here, this might be a better option.