tags:

views:

743

answers:

3

Hi,

I would like to achieve something similar to the following in Guice:

public MyClass {

    private final InjectedObject[] injectedObjects;

    @Inject
    public MyClass(InjectedObject[] injectedObjects) {
        this.injectedObjects=injectedObjects;
    }
}

ie I would like to be able to create a certain number of instances of an object, and inject them into another object as an array. I could probably do this instead:

public MyClass {

    private final InjectedObject[] injectedObjects;

    @Inject
    public MyClass(InjectedObjectProvider injectedObjectProvider) {
        this.injectedObjects=injectedObjectProvider.getArrayOfInjectedObjects(5);
    }
}

...but I was wondering if there was another route that was more elegant?

Thanks in advance for any suggestions/ideas.

Cheers

Rich

+3  A: 

I'm curious why you want several objects created eagerly. You might have success injecting a Provider<InjectedObject>, and calling Provider.get() each time you need an instance. If you really need 5, you could build 'em in a loop:

public MyClass {
  private final List<InjectedObject> injectedObjects;

  @Inject
  public MyClass(Provider<InjectedObject> injectedObjectProvider) {
    injectedObjects = new ArrayList<InjectedObject>();
    for (int i = 0; i < 5; i++) {
      injectedObjects.add(injectedObjectProvider.get());
    }
  }
}
Jesse Wilson
Thanks for the suggestion - the reason for having several provided eagerly is to be able to place them into a ThreadPool - the number of instances in the pool will depend on the number of cores available.
Rich
+1  A: 

One option would be to inject a Provider<InjectedObject> into your class, as Jesse mentioned:

public class MyClass {
  private final List<InjectedObject> injectedObjects;

  @Inject
  public MyClass(Provider<InjectedObject> injectedObjectProvider) {
    List<InjectedObject> objects = new ArrayList<InjectedObject>();
    for (int i = 0; i < 5; i++) {
      objects.add(injectedObjectProvider.get());
    }
    injectedObjects = Collections.unmodifiableList(objects);
  }
}

Doing this can be problematic. If InjectedObject is scoped as @Singleton or @RequestScoped, then each time you call injectedObjectProvider.get() you would get the same reference. Another problem with injecting a Provider to do this is it wouldn't be clear from the API that MyClass depends on multiple instances of InjectedObject. Finally, you've hardcoded in MyClass that it needs to be injected with five instances.

It's rare that you will need to inject a Provider into an object. Usually when I do this, it's because the scope of the current object means that it will be longer-lived than the scope of the dependent object (for instance, a @Singleton that needs access to a @RequestScoped object).

Instead of injecting a Provider, you can inject a List<InjectedObject> into the constructor, and create a provider method in a Guice module:

@Provides
MyClass prividesMyClass(Provider<InjectedObject> injectedObjectProvider) {
  List<InjectedObject> objects = new ArrayList<InjectedObject>();
  for (int i = 0; i < 5; i++) {
   objects.add(injectedObjectProvider.get());
  }
  return new MyClass(objects);
}

(you could, of course, bind using a TypeLiteral)

Why is this better? Even though you are still hard-coding five objects in this code, it isn't hard-coded in MyClass, so clients of MyClass (including the tests for MyClass itself) can choose to construct the object in different ways.

If hard-coding this knowledge in a Guice module isn't a good idea, you can instead create an interface that has a more specific contract than Provider

public interface InjectedObjectRepository {
  List<InjectedObject> getInjectedObjects();
}

Even if you decide that you want MyClass to be responsible for knowing how many instances to create, you might want to create an interface (perhaps named InjectedObjectSupplier so you could document explicitly that you expect a unique instance every time.

NamshubWriter
+4  A: 

Not sure if this suits your needs, but Multibindings worked for me when I needed to inject multiple elements of the same kind (it produces a set though).

Eelco