I'm trying to combine this 3 features of Guice: inject, multibinding, generics. I create a prototype of production project, so here it is:
First, this is a little hierarchy for generics(in production case there is hierarchy of N entities):
public interface Type {
}
public class Type1 implements Type{
}
public class Type2 implements Type {
}
Next, classes ToCreate1 and ToCreate2 I want to create by Factory.
Base class:
public abstract class AbstractToCreate<T extends Type> {
public T type;
public Integer param;
public AbstractToCreate(T type, Integer param){
this.type = type;
this.param = param;
}
}
It's inheritors:
public class ToCreate1 extends AbstractToCreate<Type1>{
@Inject
public ToCreate1(Type1 type, @Assisted Integer param) {
super(type, param);
}
}
public class ToCreate2 extends AbstractToCreate<Type2> {
@Inject
public ToCreate2(Type2 type, @Assisted Integer param) {
super(type, param);
}
}
Then, the Factory itself:
public interface Factory<T extends Type> {
AbstractToCreate<T> create(Integer param);
}
So, now I want to Inject a map, containing Factory<Type1> and Factory<Type2> to create ToInject1 and ToInject2 respectively.
So, I create Guice's AbstractModule with configure method:
protected void configure() {
install(new FactoryModuleBuilder()
.implement(new TypeLiteral<AbstractToCreate<Type1>>(){}, ToCreate1.class)
.build(new TypeLiteral<Factory<Type1>>(){}));
install(new FactoryModuleBuilder()
.implement(new TypeLiteral<AbstractToCreate<Type2>>(){}, ToCreate2.class)
.build(new TypeLiteral<Factory<Type2>>(){}));
MapBinder<String, Factory> mapBinder = MapBinder.newMapBinder(binder(), String.class, Factory.class);
mapBinder.addBinding("type1").to(new TypeLiteral<Factory<Type1>>(){});
mapBinder.addBinding("type2").to(new TypeLiteral<Factory<Type2>>(){});
}
So, I inject it @Inject public Map<String, Factory> map;
and all is Ok:
Factory<Type1> factory1 = main.map.get("type1");
Factory<Type2> factory2 = main.map.get("type2");
AbstractToCreate<Type1> create1 = factory1.create(1);//create1 is ToCreate1 instance
AbstractToCreate<Type2> create2 = factory2.create(2);//create2 is ToCreate2 instance
As I mentioned before, there is much more Types in my production system, so the AbstractModule becomes too cumbersome. I tried to avoid duplicate code and modified configure method:
@Override
protected void configure() {
this.<Type1>inst(ToCreate1.class);
this.<Type2>inst(ToCreate2.class);
}
private <V extends Type> void inst(Class<? extends AbstractToCreate<V>> clazz) {
install(new FactoryModuleBuilder()
.implement(new TypeLiteral<AbstractToCreate<V>>(){}, clazz)
.build(new TypeLiteral<Factory<V>>(){}));
}
And it doesn't work! Guice says:
1) ru.test.genericassistedinject.AbstractToCreate<V> cannot be used as a key; It is not fully specified.
What's wrong?
Types.newParameterizedType
instead. Check out the rest of Types, too; it's a miniature complement to TypeToken's APIs. – Zillion