Search code examples
javareflectionreflections

Getting subclasses from Class<?>


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<?>


Solution

  • 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 a Set<?>?

    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);