Guice: automate generic class binding

Can you automate generic class bindings? Consider this:

Common interface:

public interface IAction<T> {
     T echo(T inst);
}

      

Long subtype:

public class LongAction implements IAction<Long> {    
    @Override
    public Long echo(Long inst) {return inst;}
}

      

String subtype:

public class StringAction implements IAction<String> {
    @Override
    public String echo(String inst) {return inst;}
}

      

Custom Module: public class CustomModule extends AbstractModule {

@Override
protected void configure() {
  //do this automagically?
  bind(new TypeLiteral<IAction<String>>() {}).to(StringAction.class);
  bind(new TypeLiteral<IAction<Long>>() {}).to(LongAction.class);
  //
}

      

}

home

// i know i can obtain the correct instance
IAction<String> is = injector.getInstance(new Key<IAction<String>>(){});

      

Is it possible to bind in some way (like a base abstract class, reflection or whatever) the binding of the classes StringAction

and LongAction

? I've tried using reflection to no avail.

+3


source to share


2 answers


I somehow managed to trick the compiler.



class CustomModule extends AbstractModule {

static class Holder<T> {
    Class<T> param;
    Class<IAction<T>> klass;

    Holder(Class<?> p, Class<IAction<T>> k) {
        param = (Class<T>) p;
        klass = k;
    }
}

// this would be similar to the result of classpath scanning
List<IAction<?>> stuff;

public <T> void bindSome(Class<T> typeParameter, Class<IAction<T>> implementation) {
    Type parameterizedType = Types.newParameterizedType(IAction.class, typeParameter);
    bind((TypeLiteral<IAction<T>>) TypeLiteral.get(parameterizedType)).to(implementation);
}

public CustomModule() {
    stuff = new ArrayList<>();
    stuff.add(new StringAction());
    stuff.add(new LongAction());
}

@Override
protected void configure() {
    for (IAction<?> act : stuff) {
        System.out.printf("Configuring %s for %s\n", act.getTypeArguments(), act.getClass());
        //the following doesn't work because the compiler cannot understand that 
        //the 1st argument T is the same T in 2nd argument Class<T>
        //bindSome(act.getTypeArguments(), act.getClass());

        //w00t? compiler is tricked?? <String> is erased, but the holder preserves proper class?
        Holder<String> holder = new Holder(act.getTypeArguments(), (Class<IAction<String>>) act.getClass());
        bindSome(holder.param, holder.klass);

        //this is what I want to avoid doing manually
        //bind(new TypeLiteral<IAction<String>>() {}).to(StringAction.class);
    }
}
}

      

0


source


You will need to do some sort of class scanning if you don't want to explicitly list the implementations. For example Guava has ClassPath support , eg.



Note that scanning against classes is somewhat contrary to Guice's design philosophy. Is it really so much effort to add one line to the module for every implementation you write?

0


source







All Articles