Have a common provider for subclasses using Guice?

2019-07-31 10:40发布

I am trying to implement a common provider for all the subclasses, imagine some schema: SuperComponent.class is the parent of ComponentA.class and ComponentB.class. I have the provider:

    @Provides
<T extends SuperComponent> List<T> providesComponents(Provider<T> provider) {
    List<T> componentList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        componentList.add(provider.get());
    }
    return componentList;
}

The idea is to call this provider when is required the objects List<ComponentA> and/or List<ComponentB> in another class' constructor. Imagine something like:

public class ResourceManager {

List<ComponentA> componentAList;
List<ComponentB> componentBList;    

@Inject
public ResourceManager(List<ComponentA> componentAList, List<ComponentB> componentBList) {
    this.componentAList = componentAList;
            this.componentBList = componentBList;
}

I get an error saying:

1) com.google.inject.Provider<T> cannot be used as a key; It is not fully specified.

How can I make it work? I know I can do it creating different providers for each of the List<ComponentA> and List <ComponentB> but I need it as in the reality the number of components is much larger than 2...

1条回答
姐就是有狂的资本
2楼-- · 2019-07-31 11:11

I don't think there's a good built-in way to handle this--Guice can bind a lot, or inspect and manipulate its bindings, but does not have good interfaces to create meta-level bindings. You do have a few options, though:

  1. Leave the List-constructing to the consumer, through Providers:

    public static <T> List<T> createList(Provider<T> provider) {
      List<T> list = new ArrayList<T>();
      for (int i = 0; i < 5; i++) {
        list.add(provider.get());
      }
      return list;
    }
    
    @Inject MyConsumer(Provider<Foo> depProvider) {
      List<Foo> myFoos = createList(depProvider);
    }
    
  2. List out the classes you need to bind like that and create the Providers in your configure method:

    public class MyModule extends AbstractModule {
      public void configure() {
        List<Class<?>> classList = Lists.newArrayList(Class1.class, Class2.class);
        for (Class<?> clazz : classList) {
          bind(listOf(clazz)).toProvider(new ComponentListProvider<?>(getProvider(clazz)));
        }
      }
    
      private static <T> Key<List<T>> listOf(Class<T> clazz) {
        return new Key<List<T>>() {};
      }
    
      private static class ComponentListProvider<T> implements Provider<List<T>>() {
        private final Provider<T> wrappedProvider;
    
        ComponentListProvider(Provider<T> wrappedProvider) {
          this.wrappedProvider = wrappedProvider;
        }
    
        @Override public List<T> get() {
          return createList(wrappedProvider);
        }
      }
    }
    

    This uses getProvider, an extremely handy method that retrieves a typed Provider that will work as soon as the Injector is created (but not before).

  3. Write a module that does one of the above by iterating across every binding in the Module using Guice SPI.

Hope that helps!

查看更多
登录 后发表回答