My setup is a bit unconventional I believe, so the explanation will be a bit longer. I have an abstract super class, with the method
public abstract T execute(P parameters);
I have a group of implementation classes which extend it, and assign individual types in the extension statement. Meaning the implementation is not generic.
I grab all Implementations via annotation @Example. I use ClassGraph to pull the information about these classes. Through it I get not only the implementation, but also the correct superclass with the defined Types.
However I now fail to use Guice bind().to();
To give a bit more background...
I have a set of Implementation classes, which communicate with an external API. Each class communicates with a separate area. I want my own service API to not be tied to a specific implementation. I can't see the future, and there is a good chance that we need to bind implementations more flexible later (e.g. have 3 different implementation for the same call). The external service is highly flexible, so the consumer must have a similar flexibility.
For this reason, my goal was to use the guice annotated binding
bind(super.class).annotatedWith(Names.named(path)).to(implementation.class);
On the other side, in my own Service API I then wanted to user method injection with @Named annotation to define the binding reception.
@Inject
public void setImplementation(@Named(path) Super<InputType, OutputType> implementation){...}
Problem with above approach was the generic . The super.class binding doesn't account for those, so no binding was found.
I've read up on generic binding in Guice next with the TypeLiteral. However I did not find a way to make even the compiler happy.
From ClassGraph I receive the ClassInfo. So I tried following setups
//class list is the result of ClassGraph
for (Class<?> entry : classList){
Class<? extends Super> implementationClass = (Class<? extends Super> entry;
String value = entry.getAnnotation(Example.class).value();
TypeLiteral<? extends Super> typeLiteral = TypeLiteral.get(implementationClass);
TypeLiteral<?> supertype = typeLiteral.getSupertype(Super.class);
//now the trials start and all are wrong, annotation is left out for now
bind(impelmentationClass.getSuperclass()).to(implementationClass);
bind(impelmentationClass.getSuperclass()).to(entry);
bind(impelmentationClass.getSuperclass()).to(typeLiteral);
bind(supertype).to(implementationClass);
bind(supertype).to(entry);
bind(supertype).to(typeLiteral);
}
Is what I'm trying just not possible, or am I using TypeLiteral wrong? I did search for two days and tried much more different approaches (this is just the closest to home). So I'm somewhat at an impasse, and I'm hoping someone else faced this before :)
Any help is appreciated
I just gave this a shot (it may not be exactly the same) but this seemed to work in my environment:
public abstract class ChequePrinter<T> implements Printer<T, PrintLayout> {
public abstract print(T cheque);
}
public VoidChequePrinter extends ChequePrinter<VoidCheque> {
public print (VoidCheque cheque);
}
pretty straight forward, except I'm printing to a PrintLayout instead of a network request, but that shouldn't matter. Now for my example I just hard coded the class definition, instead of pulling them in from ClassGraph, but as long as:
Class<?> entry is a regular class
it should be ok. Here is what I just used:
// should be the same as entry
Class<? extends ChequePrinter<?>> clazz = VoidChequePrinter.class;
TypeLiteral typeLiteral = TypeLiteral.get(clazz.getGenericSuperClass());
bind(typeLiteral).annotatedWith(Names.named("Void")).to(clazz);
In my test class VoidChequePrinterTest
I have:
@Inject @Named("Void") ChequePrinter<VoidCheque> printer;
which works as I would expect with the correct results.