How is Java unable to interpret this code:
private <T> Class<T> getClass(T object) {
return object.getClass();
}
I'm trying to visualise this and when I substitute in for an exmaple
private Class<String> getClass(String s) {
return s.getClass();
}
I get the same sort of error: required type <String>
provided type <? extends String>
. I've seen code that gets around this error by doing:
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) object.getClass();
What does this mean? What am I not understanding about this generics problem?
CodePudding user response:
getClass
follows some specific rules that are a bit unusual. The declared return type of Object::getClass is simply Class<?>
, i.e. a class of some type. But as per the docs
The actual result type is
Class<? extends |X|>
where |X| is the erasure of the static type of the expression on whichgetClass
is called.
The erasure for T in your example is simply Object, so the return is Class<? extends Object>
. Everything extends Object, so that's the same as Class<?>
.
When the parameter is the specific type String, the erasure is also String, and the result is Class<? extends String>
.
Oracle tutorial on type erasure
I guess what happens under the hood is that the compiler effectively "overrides" Object::getClass with a more specific return type. Child classes are allowed to return more specific types than their parents, but not less specific. Consider this valid code:
class Foo {
public Class<? extends List<?>> foo() { return null; }
}
class Bar extends Foo {
@Override
public Class<? extends ArrayList<?>> foo() { return null; }
}
Because of how this is implemented, T obj; obj.getClass();
will return Class<?>
, rather than what you're expecting which is Class<? extends T>
.
In your case, the cast is safe.
CodePudding user response:
As types are still erase you just have an Object object
.
Class<?> type = object.getClass();
It is the other way around, one may use:
private <T> T getObject(Object object, Class<T> type) {
return type.cast(object);
}
The above does a dynamic runtime cast. This might be useful:
private final Map<Class<?>, Object> singletonsByType = new HashMap<>();
public <T> void store(Class<T> type, T object) {
singletonsByType.put(type, object);
}
public <S> S lookup(Class<S> type) {
return type.cast(singletonsByType.get(type));
}
CodePudding user response:
You can salve that problem like this:
private <T> Class<T> getClass(T object) {
return (Class<T>)object.getClass();
}