代码之家  ›  专栏  ›  技术社区  ›  Sean Carpenter

使用Guice注入通用实现

  •  46
  • Sean Carpenter  · 技术社区  · 15 年前

    我希望能够使用Guice注入通用接口的通用实现。

    public interface Repository<T> {
      void save(T item);
      T get(int id);
    }
    
    public MyRepository<T> implements Repository<T> {
      @Override
      public void save(T item) {
        // do saving
        return item;
      }
      @Override
      public T get(int id) {
        // get item and return
      }
    }
    

    to do :

    Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>))
    

    但我不认为在Guice中存在类似的东西。我知道我可以用 TypeLiteral

    编辑:

    下面是一个用法示例:

    Injector injector = Guice.createInjector(new MyModule());
    Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {});
    Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {});
    

    尽管更可能的用法是注入到另一个类中:

    public class ClassThatUsesRepository {
      private Repository<Class1> repository;
    
      @Inject
      public ClassThatUsesRepository(Repository<Class1> repository) {
        this.repository = repository;
      }
    }
    
    4 回复  |  直到 13 年前
        1
  •  68
  •   Ruslan Batdalov    9 年前

    为了在Guice中使用泛型,您需要使用 TypeLiteral 类来绑定泛型变量。这是一个关于您的Guice注入器配置的示例:

    package your-application.com;
    
    import com.google.inject.AbstractModule;
    import com.google.inject.TypeLiteral;
    
    public class MyModule extends AbstractModule {
      @Override
      protected void configure() {
        bind(new TypeLiteral<Repository<Class1>>(){})
          .to(new TypeLiteral<MyRepository<Class1>>(){});
      }
    }
    

        2
  •  5
  •   SGal    9 年前

    泛型在运行时没有被保留,这使得最初理解这个概念更加困难。 不管怎样,这是有原因的 new ArrayList<String>().getClass() 回报 Class<?> 而不是 Class<String> Class<? extends String> 您应该记住,泛型只是用于编译时类型检查(如果您愿意的话,有点像隐式验证)。

    MyRepository (使用任何类型)实现,只要需要 Repository

    下面是一个代码运行良好的示例:

    public class GuiceTest extends AbstractModule {
    
        @Inject
        List collection;
    
        public static void main(String[] args) {
            GuiceTest app = new GuiceTest();
            app.test();
        }
    
        public void test(){
            Injector injector = Guice.createInjector(new GuiceTest());
            injector.injectMembers(this);
    
            List<String> strCollection = collection;
            strCollection.add("I'm a String");
            System.out.println(collection.get(0));
    
            List<Integer> intCollection = collection;
            intCollection.add(new Integer(33));
            System.out.println(collection.get(1));
        }
    
        @Override
        protected void configure() {
            bind(List.class).to(LinkedList.class);
        }
    }
    

    I'm a String
    33
    

    但那张单子 LinkedList . 尽管在这个例子中,如果你试图 内景 是什么东西 字符串 你会得到一个例外。

    int i = collection.get(0)
    

    但是如果你想得到一个已经被类型casted和dandy注入的对象,你可以要求 List<String> 列表<字符串> 属于 ArrayList<String> 实施和 List<Integer> 属于 LinkedList<Integer> ,Guice允许你这样做(没有经过测试,有根据的猜测)。

    但有一个陷阱:

        @Override
        protected void configure() {
            bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening*
        }
    

    类文字不是泛型的 . 那是你用Guice的地方 TypeLiterals .

        @Override
        protected void configure() {
            bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){});
        }
    

    字体 保留泛型类型变量作为元信息的一部分,以映射到所需的实现。希望这有帮助。

        3
  •  0
  •   jedediah    8 年前

    你可以使用(虐待?)这个 @ImplementedBy 使Guice为您生成通用绑定的注释:

    @ImplementedBy(MyRepository.class)
    interface Repository<T> { ... }
    
    class MyRepository<T> implements Repository<T> { ... }
    

    只要启用了即时绑定,就可以注入 Repository<Whatever> 没有任何显式绑定:

        Injector injector = Guice.createInjector();
        System.out.println(injector.getBinding(new Key<Repository<String>>(){}));
        System.out.println(injector.getBinding(new Key<Repository<Integer>>(){}));
    

    关键是绑定的目标是 MyRepository MyRepository<T> :

    LinkedKeyBinding{key=Key[type=Repository<java.lang.String>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]}
    LinkedKeyBinding{key=Key[type=Repository<java.lang.Integer>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]}
    

    这通常不是问题,但它意味着 我的知识库 TypeLiteral<T> 在运行时找出它自己的类型,这在这种情况下特别有用。除此之外,据我所知,这样做还不错。

    (如果有人想解决这个问题,我敢肯定只需要额外的计算 around here

        4
  •  0
  •   ohad serfaty    7 年前

    有点相关,希望有人会发现这有用。在某些情况下,特别是当您有要泛化的类型的java.lang.Class实例时,可以通过扩展parametedType类在运行时强制注入。

    在下面的解决方案中,工厂方法创建 地图<K,V> 给定类对象的实例

    示例.java:

    @SuppressWarnings("unchecked")
    public class Example<K extends Number> {
    
      Injector injector = ...
    
      public Set<K> foo(Class<K> klass) {
        CompositeType typeLiteral = new CompositeType(Set.class, klass);
        Set<K> set = (Set<K>) injector.getInstance(Key.get(typeLiteral));
        return set;
      }
    
      public <V> Map<K,V> bar(Class<K> keyClass, Class<V> valueClass) {
        CompositeType typeLiteral = new CompositeType(Map.class, keyClass, valueClass);
        Map<K,V> collection = (Map<K,V>) injector.getInstance(Key.get(typeLiteral));
        return collection;
      }
    }
    

    composetype.java:

    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    import org.apache.commons.lang.StringUtils;
    
    public class CompositeType implements ParameterizedType {
    
      private final String typeName;
      private final Class<?> baseClass;
      private final Type[] genericClass;
    
      public CompositeType(Class<?> baseClass, Class<?>... genericClasses) {
        this.baseClass = baseClass;
        this.genericClass = genericClasses;
        List<String> generics = ((List<Class<?>>)Arrays.asList(genericClasses))
                .stream()
                .map(Class::getName)
                .collect(Collectors.toList());
        String genericTypeString = StringUtils.join(generics, ",");
        this.typeName = baseClass.getName() + "<" + genericTypeString + ">";
      }
    
      @Override
      public String getTypeName() {
        return typeName;
      }
    
      @Override
      public Type[] getActualTypeArguments() {
        return genericClass;
      }
    
      @Override
      public Type getRawType() {
        return baseClass;
      }
    
      @Override
      public Type getOwnerType() {
        return null;
      }
    }