I am using Guice and FactoryModuleBuilder. Typically, it is enough to just define an interface of the factory and Guice will automatically inject the implementation.
However, the part that I am struggling is that the methods in a factory uses generics. Suppose I have the following. A base type of constructed instances as defined by the interface.
interface Foo<T> {
T get();
}
And two implementations of the Foo
interface as defined by the two classes below.
class FooA<T> implements Foo<T> {
@Inject
FooA(@Assisted Class<T> clazz, @Assisted String s) {...}
}
class FooB<T> implements Foo<T> {
@Inject
FooB(@Assisted Class<T> clazz, @Assisted Integer i) {...}
}
Then I have the factory interface as defined below, using two custom binding annotations that allows me to use multiple implementations.
interface FooFactory {
@A Foo<T> build(Class<T> clazz, String s);
@B Foo<T> build(Class<T> clazz, Integer i);
}
I have tried a number of possible solutions, all but one has worked so far. The solution that worked is to basically write my own implementation of FooFactory
as shown below. And in the configure
method of the module, bind the interface to the implementation; bind(FooFactory.class).to(FooFactoryImpl.class);
class FooFactoryImpl {
Foo<T> build(Class<T> clazz, String s) {
return new FooA(clazz, s):
}
Foo<T> build(Class<T> clazz, Integer i) {
return new FooB(clazz, i);
}
}
However, I have one issue with this solution. The instances are not created by Guice and thus I lose the null checks that comes with Guice. This is drastically different from my other factories that does not have this problem. This means I have to explicitly write null checks for every implementation of Foo
. I would like to avoid that.
The following are some of the solutions I have tried.
Solution 1:
FactoryModuleBuilder fmb = new FactoryModuleBuilder()
.implement(Foo.class, A.class, FooA.class)
.implement(Foo.class, B.class, FooB.class);
install(fmb.build(FooFactory.class));
Solution 2:
FactoryModuleBuilder fmb = new FactoryModuleBuilder()
.implement(TypeLiteral.get(Foo.class), A.class, TypeLiteral.get(FooA.class))
.implement(TypeLiteral.get(Foo.class), B.class, TypeLiteral.get(FooB.class));
install(fmb.build(TypeLiteral.get(FooFactory.class)));
The sample code is available at GitHub (if anyone is interested).