getClass() of a Generic Method Parameter in a Java
The following Java method fails to compile:
<T extends Number> void foo(T t)
{
Class<? extends T> klass = t.getClass();
}
Error received is:
Type mismatch: cannot convert from Class<capture#3-of ? extends Number>
to Class<? extends T>
Can someone explain why Class<? extends T>
is invalid, but Class<? exten开发者_运维百科ds Number>
is fine?
Javadoc says:
The actual result type is
Class<? extends |X|>
where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
Because the T
class' type doesn't extend from T
. Instead, it extends from Number
, exactly as you have declared yourself in <T extends Number>
. Simple as that.
A better question would be:
Why doesn't the following compile?
<T extends Number> void foo(T t) { Class<T> class1 = t.getClass(); }
The answer to that is that the Object#getClass()
returns Class<?>
with an unbounded wildcard ?
because the object itself is not directly aware about its generic type which is been expected in an arbitrary method.
It is kind of silly. It would have been better, for most use cases, if x.getClass()
returns Class<? extends X>
, instead of the erased Class<? extends |X|>
.
The erasure is the cause of loss of information, making your code, apparently safe, fail to compile. t.getClass()
returns Class<? extends |T|>
, and |T| = Number
, so it returns Class<? extends Number>
.
The erasure (mandated by the language spec) is to maintain theoretical correctness. For example
List<String> x = ...;
Class<List> c1 = x.getClass(); // ok
Class<List<String>> c2 = x.getClass(); // error
Although c2 seems very reasonable, in Java, there is really no such class for List<String>
. There is only the class for List
. So allowing c2 would be, theoretically incorrect.
This formality created lots of problem in real world usages, where programmers can reason that Class<? extends X>
is safe for their purposes, but have to cope with the erased version.
You can simply define your own getClass
that returns the un-erased type
static public <X> Class<? extends X> getFullClass(X x)
return (Class<? extends X>)(Class) x.getClass() ;
<T extends Number> void foo(T t)
{
Class<? extends T> klass = getFullClass(t);
}
精彩评论