PUBLIC OBJECT

What's a Hierarchical Injector?

Our application has two implementations for one interface. EnergySource is implemented by both Plutonium and LightningBolt:

class DeLorean {
  @Inject TimeCircuits timeCircuits;
  @Inject FluxCapacitor fluxCapacitor;
  @Inject EnergySource energySource;
}

interface FluxCapacitor {
  boolean isFluxing();
}

@Singleton
class RealFluxCapacitor implements FluxCapacitor {
  @Inject TimeCircuits timeCircuits;
  boolean isFluxing;

  public boolean isFluxing() {
    return isFluxing;
  }
}

class TimeCircuits {
  Date whereYouveBeen;
  Date whereYouAre;
  Date whereYourGoing;
}

interface EnergySource {
  void generateOnePointTwentyOneGigawatts();
}

class Plutonium implements EnergySource { ... }

class LightningBolt implements EnergySource { ... }`</pre>
And to allow for sequels, we assume other implementations of `EnergySource` are possible. We'd like to create an `Injector` immediately and create a Plutonium-powered DeLorean. Shortly thereafter, we'd like to re-use that same `Injector`, but with a lightning bolt for energy.
> ![](/uploaded_images/plutonium.png)

### Option one: Factory classes

We can solve this problem by introducing a `DeLorean.Factory` interface that accepts an `EnergySource` as its only parameter:
<pre class="prettyprint">`class DeLorean {
  private final TimeCircuits timeCircuits;
  private final FluxCapacitor fluxCapacitor;
  private final EnergySource energySource;

  DeLorean(TimeCircuits timeCircuits, 
      FluxCapacitor fluxCapacitor,
      EnergySource energySource) {
    this.timeCircuits = timeCircuits;
    this.fluxCapacitor = fluxCapacitor;
    this.energySource = energySource;
  }

  static class Factory {
    @Inject TimeCircuits timeCircuits;
    @Inject FluxCapacitor fluxCapacitor;

    DeLorean create(EnergySource energySource) {
      return new DeLorean(timeCircuits, fluxCapacitor, energySource);
    }
  }
}`</pre>
This works for our _specific_ problem, but in general it's quite awkward:
  • It requires a gross amount of boilerplate code.

  • It discourages refactoring of the DeLorean class.* It increases the complexity of getting an EnergySource.

  • It doesn't work unless EnergySource is a direct dependency of the DeLorean class. Otherwise you need to create lots of little factories that cascade.* And EnergySource is no longer in-the-club—it doesn't participate in Guice's injection, AOP, scoping, etc.

    Option two: AssistedInject

    AssistedInject is a Guice extension that's intended to reduce the boilerplate of option one. Instead of a factory class, we write a factory interface plus annotations:

    `class DeLorean {
      TimeCircuits timeCircuits;
      FluxCapacitor fluxCapacitor;
      EnergySource energySource;
    

    @AssistedInject
    DeLorean(TimeCircuits timeCircuits,
    FluxCapacitor fluxCapacitor,
    @Assisted EnergySource energySource) {
    this.timeCircuits = timeCircuits;
    this.fluxCapacitor = fluxCapacitor;
    this.energySource = energySource;
    }

    interface Factory {
    DeLorean create(EnergySource energySource);
    }
    }</pre> This fixes some problems. But the core issue still remains: getting an instance of EnergySource is difficult. Unlike regular Guice (@Injectis the newnew), you need to change all callers if you add a dependency on EnergySource`.

    Option three: Hierarchical Injectors

    The premise is simple. @Inject anything, even stuff you don't know at injector-creation time. So our DeLorean class would look exactly as it would if EnergySource was constant:

    `class DeLorean {
      TimeCircuits timeCircuits;
      FluxCapacitor fluxCapacitor;
      EnergySource energySource;
    

    @Inject
    DeLorean(TimeCircuits timeCircuits,
    FluxCapacitor fluxCapacitor,
    EnergySource energySource) {
    this.timeCircuits = timeCircuits;
    this.fluxCapacitor = fluxCapacitor;
    this.energySource = energySource;
    }
    }</pre> To use it, we start with an Injectorthat had bindings for everything _except_ forEnergySource. Next, we create a second injector that extends the first, and binds either PlutoniumorLightningBolt`. This second injector fills in its missing binding.

    The injectors share singletons, so we don't have to worry about having multiple TimeCircuits. Static analysis is applied to both injectors as a whole, where complete information is known. And all objects are in-the-club and get Guice value-adds like injection, scoping and AOP.

    This is the solution to the mystical Robot Legs problem, wherein we have a RobotLeg class, that needs be injected with either a LeftFoot or a RightFoot, depending on where the leg will ultimately be used.

    Criticism of Hierarchical Injectors

    They suggest competing bindings. One parent injector could have relations with multiple child injectors. In our example, the parent injector binds DeLorean and TimeCircuits, and each child binds a different EnergySource.

    They require abstract Injectors. The parent injector in our example wouldn't be able to create an instance of DeLorean, since it doesn't have all of the prerequisite bindings. This is just weird.

    They're complex. Guice was born out of making code simpler. Does the conceptual weight of hierarchical injectors justify their inclusion?

    Going forward

    Today's Guice includes a simplified implementation of hierarchical injectors written by Dan Halem. It doesn't cover the interesting (but complex) case where the parent injector cannot fulfill all of its bindings. I'm studying the use cases, trying to come up with a balance between ease-of-use and power.

    For example, one idea is to require users to explicitly call-out bindings that child injectors will provide:

    `  public void configure() {
        bind(EnergySource.class).throughChildInjector();
      }
    

I'd also like to do something similar to AssistedInject's factory interfaces. This way the second injector would be created, used and discarded transparently, so the user never needs to see it. From the user's perspective, this would just be like AssistedInject, but the assisted parameters could be injected anywhere.

If you have suggested use-cases or ideas, I'd love to hear 'em.