Reputation: 31
I am having an issue with generics and what I presume is type erasure whilst trying to reflect some generic information.
I am using a class based on http://www.artima.com/weblogs/viewpost.jsp?thread=208860 to do the reflection, but that isn't the issue.
The code below demonstrates the issue:
private <U extends MyClass1> U doSomething(MyClass2<U> p) {
Class<U> classIWant = null;
for (Class<?> c : GenericTypeUtils.getTypeArguments(MyClass2.class, p.getClass())) {
if (MyClass1.class.isAssignableFrom(c)) {
classIWant = c.asSubclass(MyClass1.class);
break;
}
}
}
Unfortunately the line
classIWant = c.asSubclass(MyClass1.class);
Shows the following error
java: incompatible types
required: java.lang.Class<U>
found: java.lang.Class<capture#4 of ? extends MyClass1>
And that's really confusing me. U extends MyClass1 and therefore U is a MyClass1.
Obviously there is some piece of type erasure I am not understanding here - can anybody help, please. BTW this is Java 1.7.0_40
Thanks
Conrad Winchester
Upvotes: 3
Views: 137
Reputation: 178243
The Java compiler is unable to guarantee that the Class
object c
matches the generic type U
. Because of type erasure, it is up to the compiler to guarantee type safety, and ensure that the Class
coming back from c.asSubclass
matches Class<U>
, but it can't.
The asSubclass
method returns a Class<? extends U>
. In the Class
class, U
represents the type of Class
passed into asSubclass
. When you call it, that is c
. But the compiler cannot guarantee that this return type matches your own declared <U extends MyClass1>
. They could be different subclasses of MyClass1
.
If you can guarantee that c.asSubclass
will return a Class<U>
, then cast it as a Class<U>
:
classIWant = (Class<U>) c.asSubclass(MyClass1.class);
You will receive an "unchecked cast" warning. This is there because the compiler cannot guarantee that is really is a Class<U>
, but you are saying that it is. Be careful as this may result in a hard-to-track-down ClassCastException
elsewhere, even after you return the U
. Do this only if you can guarantee the type safety yourself.
Upvotes: 1
Reputation: 279880
The method asSubclass()
is implemented like so
public <U> Class<? extends U> asSubclass(Class<U> clazz) {
if (clazz.isAssignableFrom(this))
return (Class<? extends U>) this;
else
throw new ClassCastException(this.toString());
}
with a return type of Class<? extends U>
. Note, that your U
and the U
in the asSubclass
method are completely unrelated type variables.
So the method asSubclass()
returns a value declared with the type Class
of some unknown subtype of U
, where U
in this case is MyClass1
.
You are trying to assign this value to a variable of type Class
with some known subtype of MyClass1
. The compiler can't guarantee that they match and therefore doesn't allow compilation.
You can cast the returned value if you wish, but may end up getting ClassCastException
at runtime, if the types don't match.
Upvotes: 1