0

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

Community
  • 1
  • 1
Jit B
  • 1,206
  • 14
  • 26

2 Answers2

2

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);
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
1

The getSubTypesOf method is declared as

public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type) {

It is a generic method, declaring T as a type parameter. When you invoke the method, the method invocation captures a type argument, either implicitly or because it was explicitly provided.

You've invoked the method with an argument of type Class<?>. The method will capture the ? as type. The method return type will therefore become Set<Class<? extends CAP of ?>>, ie. a set of classes of some unknown type that is a subtype of some specific unknown type.

However, you are trying to assign that to a Set<Class<?>> which is a set of classes of some unknown type. But that unknown type is not necessarily a subtype of some specific unknown type. I know that doesn't make much sense when you say it out loud, but consider ? or CAP of ? as a specific type. This is the same as trying to do

Set<Class<?>> implTypes = new HashSet<Class<Integer>>();

This isn't immediately obvious since Class is final and therefore cannot be subclassed, but a HashSet<Class<Integer>> is not assignable to Set<Class<?>> in the same way that a ArrayList<ArrayList<Integer> is not assignable to a List<List<?>>.

Additional reading:

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724