0

I have an interface with many possible implementations. The right implementation should be chosen at the runtime. And so Reflection sounds to be the solution.

I have annotated these classes by a qualifier that has as argument an enumeration.

So, is it possible to get at runtime using reflection the right implementatoin class by passing the right enumeration to the annotation?

But, reflection is not mandatory if there is another way..

First, here it is the enumeration :

public enum CATEGORY {
 A,B,C;
}

Then, here it the interface :

public interface GenericI{
   void method1(CATEGORY arg);
   // some other methods
}

And now, here there are the annotated implementations :

@MyAnnotation(CATEGORY.A)
public class impl1 implements GenericI{
    void method1(CATEGORY arg){
       // some work here
    }
}

@MyAnnotation(CATEGORY.B)
public class impl2 implements GenericI{
    void method1(CATEGORY arg){
       // some work here
    }    
}

Finally, the proxy that at a way, select dynamically the right implementation using annotation and enumeration (probably it shouldn't implement GenericI ??):

public class MyProxy implements GenericI {

  // Here we must be able to select the right implementation
}
  • 2
    plese define what `the right implementation` means – Mario Stoilov May 30 '14 at 11:28
  • Are you envisaging that the user will somehow provide the class name as a String? If so, you can use `Class.forName`. If not ... well please clarify what you _do_ mean. – Dawood ibn Kareem May 30 '14 at 11:33
  • If there is some input that would allow you to arrive at the class name, using some configuration, it is better use the same, instead of reflection. Based on the input get the the class name from properties file/DB or some config and then use the Class.forName to get the correct instance of the implementation. – ganaraj May 30 '14 at 11:42
  • Hello sir! I added important details. Thank you! –  May 30 '14 at 12:55
  • You know all possible subclasses at compile time? – Arturo Volpe May 30 '14 at 13:23
  • No, I don't, but the annotation is likely helping... no? –  May 30 '14 at 13:44

1 Answers1

1

Reflexion is an answer, but you need to get all the classes from the classpath, and examinate it to find the implementation of your interface.

You can use this reflection library and get all the implementations like this (if your interface name is MyInterface):

Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

for (Class<T extends MyInterface> c : classes) {

     check if c is the rigth implementation!.
}

If you don't want to use a external library, you can use the Java Reflection API, and scan all packages, somethis like (see this answers to use instrumentation):

Instrumentation inst = InstrumentHook.getInstrumentation();
for (Class<?> c: inst.getAllLoadedClasses()) {
    if (MyInterface.class.isAssignableFrom(c)) {
         check if c is the rigth implementation!.
    }
}

The first option allow you to save the Reflections object as a xml, so the component scan is saved and it's done only one time.

To check if the clazz have a Qualifier you can use:

if (c.isAnnotationPresent(Qualifier.class)) {
     bingo!.
}

or is a property of the annotation:

if (c.isAnnotationPresent(Qualifier.class)) {
     Qualifier q = c.getAnnotation(Qualifier.class);
     if (q.theRight()) {
         bingo!
     }
}

I recommend you to see if the FactoryProblem is applycable to your problem, choose always Factory instead of Reflection.

An example "proxy":

public class MyProxy implements GenericI {

    Map<Category, GenericI> generics;

    public MyProxy() {
        Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
        Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
        generics = new HashMap<Category, GenericI>();
        for (Class<T extends MyInterface> c : classes) {

            map.put(c.getAnnotation(MyAnnotation.class).value(), c.newInstance());
        }       
    }

    void method1(CATEGORY arg){

        map.get(arg).method1(arg);
    }   

}

This is extremely heavy and overcomplicated, if you use this, please add extensive test, and make MyProxy a Singleton.

If you use a IOC framework:

@Component
public class MyProxy implements GenericI {

    @Autoriwed // If spring
    List<GenericI> generics; 

    @Inject @Any // If CDI
    private Instance<GenericI> services;

    Map<Category, GenericI> generics;

    @PostConstruct
    void makeMap() {
        generics = new HashMap<>();
        for (GenericI component : generics) {
            generics.put(
                component.getClass().getAnnotation(MyAnnotation.class).value(),
                component);
        }
    }

    void method1(CATEGORY arg){

        map.get(arg).method1(arg);
    }   

}

I assume you don't know al possible subclasses.

Community
  • 1
  • 1
Arturo Volpe
  • 3,442
  • 3
  • 25
  • 40
  • This will work as long as there is only one implementation that is right, else you will end up with an elaborate design to handle the multiple match case. – ganaraj May 30 '14 at 11:46
  • 1
    Yes, I think the `FactoryPattern` is the best solution, but the OP ask for reflection!, and in some cases, when the posible instances is not know at compile time, this can be the only solution, for example, when you're writing a library. – Arturo Volpe May 30 '14 at 11:48
  • Hello sir! I added important details. Thank you! –  May 30 '14 at 12:54
  • Oh... impressive license :) WTFPL –  May 30 '14 at 13:42
  • MyProxy might be a signleton, or an ApplicationScoped injected bean. Likely better? –  May 30 '14 at 13:43
  • Depends on your framework, what frameworkd do you use?, an ApplicationScoped is allways better than a singleton, but singleton is allways applicable. – Arturo Volpe May 30 '14 at 13:45
  • CDI injection... But anyway, this is the right answer. :). +1 –  May 30 '14 at 14:57
  • CDI!, try [this](http://stackoverflow.com/questions/4009388/inject-list-of-objects-in-cdi-weld) – Arturo Volpe May 30 '14 at 17:23