Reputation: 3030
If I have the following interface:
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<T> getElementClass();
/* more functions... */
}
Say that I want to implement this for a generic type like Collection<T>
. How do I implement getElementClass()
? My best attempt so far is something like this:
class MyCollectionProcessor<T> implements MyElementProcessor<Collection<T>> {
public void doSomethingWith(Collection<T> element) { /* ... */ }
public Class<Collection<T>> getElementClass() {
// Ugly, but the only way I have found to cast to Class<Collection<T>>:
return (Class<Collection<T>>) (Object) Collection.class;
}
}
I know that there is no way to specify a Class<Collection<T>>
literal in Java, but why isn't it possible to cast Collection.class
directly to Class<Collection<T>>
? Is there a better way than casting via Object
?
Upvotes: 2
Views: 1216
Reputation: 122439
why isn't it possible to cast Collection.class directly to
Class<Collection<T>>
?
Collection.class
has type Class<Collection>
. Java will not compile this type cast because it can be proved that it cannot succeed. There is probably no type that is a subtype of both Class<Collection>
and Class<Collection<T>>
.
In Generics, Foo<A>
is never a subtype of Foo<B>
if A and B are different and they are types (not wildcards), regardless of the relationship between A
and B
. Therefore, the only subtypes of Class<Collection>
are SubclassOfClass<Collection>
. And similarly the only subtypes of Class<Collection<T>>
are SubclassOfClass<Collection<T>>
. Since these do not intersect, there is no way this cast can theoretically succeed.
Is there a better way than casting via Object?
You could cast via Class<?>
; but it is not much better:
return (Class<Collection<T>>) (Class<?>) Collection.class;
Upvotes: 3
Reputation: 1389
Because of Java generics handling, a Class<List<Integer>>
class cannot be obtained (without unchecked cast), but will be Class<List>
or Class<List<?>>
.
Therefore, if you're working with parameterized type in your ElementProcessor
, i suggest that you change your method to be Class<? super T> getElementClass()
Two examples :
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<? super T> getElementClass();
/* more functions... */
}
SimpleElementProcessor implements MyElementProcessor<Integer> {
public void doSomethingWith(Collection<Integer> element) { /* ... */ }
public Class<Integer> getElementsClass() {
return Integer.class;
}
}
CollectionElementProcessor<E> implements MyElementProcessor<Collection<E>> {
public void doSomethingWith(Collection<Collection<E>> element) { /* ... */ }
// This works because Class<Collection<?>> is a valid substitute for Class<? super T>
public Class<Collection<?>> getElementsClass() {
return (Class<Collection<?>>) Collection.class; // Maybe the cast isn't needed
}
}
As for obtaining the elements class, you can use reflection : If your type derives MyElementProcessor you can obtain it like this :
for(Type interface : getClass().getGenericInterfaces()) {
if(interface instanceof ParameterizedType && ((ParameterizedType) interface).getRawType == MyElementProcessor.class)
Type myElementsType = ((ParameterizedType) interface).getActualTypeArguments()[0];
This only works for deriving classes, that is, anonymous classes or declared types, it won't work if you use it this way because of type erasure( this example is dummy : it is only an example) :
public <T> MyElementProcessor newInstance() {
return new MyElementProcessor<T> {
// overridden methods ...
};
}
In such a case, you will either :
Upvotes: 1
Reputation: 4872
You could pass the class literal to the constructor:
private final Class<Collection<T>> clazz;
public MyCollectionProcessor(Class<Collection<T>> clazz) {
this.clazz = clazz;
}
public Class<Collection<T>> getElementClass() {
return clazz;
}
No cast needed but an extra parameter...
Upvotes: 1