I have a project where the service interfaces are annotated with a @Service
annotation (custom annotation, not Spring) and there can be multiple implementation for each service (hibernate, mongodb, etc).
I am trying to load the implementation classes using reflection like this:
Step 1: Load all the interfaces with @Service
annotation
Step 2: Load all sub classes for each of the interfaces
Here is the code:
public static void main(String[] args) throws Exception{
Reflections reflections = new Reflections("net.jitix.cfs");
//load annotated interfaces
Set<Class<?>> types=reflections.getTypesAnnotatedWith(Service.class);
Iterator<Class<?>> typeIterator=types.iterator();
typeIterator.forEachRemaining(new Consumer<Class<?>>() {
@Override
public void accept(Class<?> type) {
if(!type.isInterface()){
typeIterator.remove();
}
}
});
for(Class<?> type:types){
//load implementation classes
Set<Class<?>> implTypes=reflections.getSubTypesOf(type); //error here
}
//rest of the code
}
The compile error I am getting: Type mismatch: cannot convert from Set<Class<? extends capture#8-of ?>> to Set<Class<?>>
As per my understanding Class<?>
signifies that the type can be anything, so I am confused why the method that requires Class<T>
cannot take Class<?>
as parameter.
Could anyone please help me understand why this is happening? Is there any possible alternatives to my approach? Thanks in advance!
EDIT: As per the comment by @MGorgon and answers by @StriplingWarrior and @Sotirios Delimanolis using the reflections method is out of question. Is there any way we can get the sub type of a type whose reference is of type Class<?>
The crux of this question has nothing to do with reflection, but rather covariance/contravariance. It's effectively:
Why can't I assign a
Set<? extends Something>
to aSet<?>
?
And the answer is that if you had a Set<?>
then the compiler would let you add an object of any type (say X
) to that set, and someone trying to retrieve that value from the original Set<? extends Something>
would get a runtime error when it turned out that your X
does not extend Something
.
The principle can be demonstrated even more simply like this:
Set<Dog> dogs = getDogs();
Set<Pet> pets = dogs; // the compiler will complain here
pets.add(new Cat("Fluffy")); // to avoid letting this happen
for(Dog dog : dogs) // which would cause an exception here
{
...
}
Since you (presumably) know that you're not planning to add anything to this set, however, it's probably safe to tell the compiler you know what you're doing through a little explicit casting:
Set<Class<?>> implTypes= (Set<Class<?>>)(Set<?>)reflections.getSubTypesOf(type);