I would expect that from the aspect of compile time as well as from the aspect of runtime it wouldn't be a problem for .getClass()
to provide a correctly-typed return value.
But I must be wrong.
public class _GetClassGenerics2 {
static class MyClass {
}
public static void main(String[] args) {
MyClass myInstance = new MyClass();
// here it works
Class<? extends MyClass> type = myInstance.getClass();
myMethod(myInstance);
}
public static <T extends MyClass> void myMethod(T instance) {
Class<? extends T> type = instance.getClass();
// java.lang.RuntimeException: Uncompilable source code - incompatible types
// required: java.lang.Class<? extends T>
// found: java.lang.Class<capture#1 of ? extends _GetClassGenerics2.MyClass>
}
}
EDIT: It doesn't work with Class<T>
and Class<? super T>
either.
As per the Javadoc of the getClass
method:
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
Here, the value for |X|
in your code snippet is MyClass
, hence instance.getClass()
is assignable to only Class<? extends MyClass>
or Class<?>
.
The reason for this specific wording is because when you say that for this variable having type T where <T extends MyClass>
, there can be multiple classes which extend MyClass
and hence capable of satisfying the T extends MyClass
criteria. Without runtime information there is no way of knowing which concrete implementation subclass of MyClass
was passed in the method. Hence to provide a generic solution, it returns <? extends MyClass>
since that would hold true for any subclass of MyClass
irrespective of what class instance is passed in.
java.lang.Class
does not represent a type (use java.lang.reflect.Type
for that). If T
, were say ArrayList<String>
then it makes no sense for there to be a Class<ArrayList<String>>
.
It's worth noting that in this particular case there is no need for the method to be generic.
public static <T extends MyClass> void myMethod(T instance) {
Is equivalent to:
public static void myMethod(MyClass instance) {
Instead of
Class<? extends T> type = instance.getClass();
you need to use
Class<? extends MyClass> type = instance.getClass();
You cannot directly use the T
here.
The reason is the method signature of Object.getClass()
(which you are invoking). It's:
public final Class<? extends Object> getClass()
So you are trying to convert from Class<? extends Object>
to Class<? extends T>
, which is not allowed (because you are down-casting). It is allowed using an explicit cast:
Class<? extends T> type = (Class<? extends T>) instance.getClass();
will work (though it generates a type safety warning).
Java does not support a generic type of <this> e.g.
Object could implement
class Object {
Class<this> getClass()
}
But there is no way for getClass() to express that it will return a type which is the class of the object. The compiler has no native understand of what this method does either.
IMHO, This behaviour should have been supported.