With Guice how to bind multiple instances of a parameterized class when implementation use generics?

3.7k views Asked by At

I was trying to avoid using my own factory in this case, but I couldn't found a way of doing this with Guice, maybe because I'm new using guice ;)

I have this interface to convert type E into type D :

public interface SerializationProvider<E extends ValueEntity, D extends ModelDTO> {

D serialize(E entity) throws MappingSerializationException;

}

Now using Dozer and a bit of reflection, I could implement this with a unique Default class that looks like this:

public class DefaultSerializationProvider<E extends ValueEntity, D extends ModelDTO>          implements
    SerializationProvider<E, D> {
private Mapper mapper;
private Class<D> dtoClass;

@Inject
public DefaultSerializationProvider(Class<D> dtoClass){
    this.dtoClass=dtoClass;
}

@Override
public D serialize(E entity) throws MappingSerializationException {
    D result=this.getNewDTOInstance();
    this.mapper.map(entity, result);
    return result;
}

protected D getNewDTOInstance() throws MappingSerializationException {
    try{
        D result=this.dtoClass.newInstance();
        return result;
    }catch(InvocationException ie){
        throw new MappingSerializationException(ie);
    }catch(IllegalAccessException iae){
        throw new MappingSerializationException(iae);
    }catch(InstantiationException iie){
        throw new MappingSerializationException(iie);
    }
}

@Inject
public void setMapper(Mapper mapper) {
    this.mapper = mapper;
}

}

Note that I inject the Mapper.

Now the question is how can I bind this in order to provides different instances of the DefaultSerializationProvider depending on different pairs E,D What I want to avoid is creating the hole bunch of subclasses with the sole purpose of defining the generics.

In pure Java I know I can provide a Factory that will provides this kind of instances like:

public interface SerializationProviderFactory {

public <E extends ValueEntity, D extends ModelDTO> SerializationProvider<E,D> get(Class<E> entityType, Class<D> dtoType);

}

A very simple implementation of this will be:

public class DefaultSerializationProviderFactory implements
    SerializationProviderFactory {
public static final DefaultSerializationProviderFactory INSTANCE=new  DefaultSerializationProviderFactory();

@Override
public <E extends ValueEntity, D extends ModelDTO> SerializationProvider<E, D> get(
        Class<E> entityType, Class<D> dtoType) {
    return new DefaultSerializationProvider<E, D>(dtoType);
}

}

Now the question is how can I implement this kind of Factory using Guice? Or are there a better way of doing this?

A simple use case of the Factory will be:

public MessureUnitDTO serializeUnit(MessureUnit unit){
    SerializationProviderFactory factory=DefaultSerializationProviderFactory.INSTANCE;
    SerializationProvider<MessureUnit, MessureUnitDTO> provider=factory.get(MessureUnit.class, MessureUnitDTO.class);
    return provider.serialize(unit);

}

Thanks in advance, Daniel

2

There are 2 answers

0
MForster On BEST ANSWER

As far as I know there is no way to bind the DefaultSerializationProvider for all possible type arguments. But if you can enumerate all type arguments that you need to convert, you can use a binding like this:

bind(new TypeLiteral<SerializationProvider<MessureUnit, MessureUnitDTO>>() {})
    .toInstance(new DefaultSerializationProvider<MessureUnit, MessureUnitDTO>(MessureUnitDTO.class));

If you prefer, you can avoid using toInstance by injecting a type literal into the DefaultSerializationProvider:

@Inject public DefaultSerializationProvider(TypeLiteral<D> dtoType) {
  this.dtoClass = (Class<D>) dtoType.getRawType();
}

Then you can use a binding like this:

bind(new TypeLiteral<SerializationProvider<MessureUnit, MessureUnitDTO>>() {})
    .to(new TypeLiteral<DefaultSerializationProvider<MessureUnit, MessureUnitDTO>>() {});
0
behinddwalls On

You can use the Key.get(TypeLiteral<T> typeLiteral> to achieve the parametrized binding to an instance.

MyClass<MyType> myClassInstanceOfMyType = new MyClass<MyType>();    
bind(Key.get(new TypeLiteral<MyClass<MyType>>(){})).toInstance(myClassInstanceOfMyType);

And then use as in constructor or member based injection:

class MyOtherClass {

  @Inject
  MyOtherClass(MyClass<MyType> myClass){}
  //stuff
}

or

class MyOtherClass {

 @Inject
 MyClass<MyType> myClass;

}