The following Java method fails to compile:
void foo(T t)
{
Class extends T> klass = t.getClass();
}
Error
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);
}