Search code examples
dependency-injectionguicetypeliteral

Getting TypeLiterals via method to reduce verbosity


I want to reduce the verbosity of binding a generic interface to several implementations based on TypeLiterals...
I have an interface FieldComputer<T extends ComputeField> where ComputeField is my model interface.

Tried extending a ShortLiteral class (see example below) to reduce the verbosity but it doesn't seem to work. would like to understand why?

// A typical Guice Module
public class ConflationModule implements Module {

  // typical overridden configure method
  public void configure(Binder binder) {

    // Works but is verbose....
    bindField_1(binder,
                new TypeLiteral<FieldComputer<ComputeFieldImpl>>(){},
                FieldComputerImpl.class);

    // Doesn't Work
    bindField_1(binder, 
                new ShortLiteral<ComputeFieldImpl>(){}, 
                FieldComputerImpl.class);

    // Doesn't Work
    bindField_2(binder, 
                new ShortLiteral<ComputeFieldImpl>(){}, 
                FieldComputerImpl.class);

  }

  private static class ShortLiteral<CF extends ComputeField> extends TypeLiteral<FieldComputer<CF>>{}

  private <CF extends ComputeField> void bindField_1(Binder binder,
            TypeLiteral<FieldComputer<CF>> typeLiteral,
            Class<? extends FieldComputer<CF>> clazz
  ) {
        binder.bind(typeLiteral).to(clazz);
  }

  private <CF extends ComputeField> void bindField_2(Binder binder,
            ShortLiteral<CF> typeLiteral,
            Class<? extends FieldComputer<CF>> clazz
  ) {
        binder.bind(typeLiteral).to(clazz);
  }
}

Solution

  • I would suggest you just create TypeLiteral programmatically, here is an example how to do it with different implementations of one interface:

    class TypeLiteralModule extends AbstractModule {
        @Override
        protected void configure() {
            customBind(String.class, StringConsumer.class);
            customBind(Integer.class, IntegerConsumer.class);
        }
    
        private <T> void customBind(Class<T> clazz, Class<? extends Consumer<T>> impl) {
            var typeLiteral = (TypeLiteral<Consumer<T>>) TypeLiteral.get(Types.newParameterizedType(Consumer.class, clazz));
            bind(impl).in(Singleton.class);
            bind(typeLiteral).to(impl);
        }
    }
    
    class StringConsumer implements Consumer<String> {
        @Override
        public void accept(String s) {
        }
    }
    
    class IntegerConsumer implements Consumer<Integer> {
        @Override
        public void accept(Integer s) {
        }
    }