3

Toy example:

public class MyModule extends AbstractModule {
  private static final Foo foo;

  public MyModule(Foo foo) {
    this.foo = foo;
  }

  @Override
  public void configure() {
    bind(Bar.class).toProvider(new Provider<Bar>() {
      @Override public Bar get() {
        return foo.getBar();
      }
    });
  }
}

This lets me lazily invoke the .getBar() method of a user-provided Foo instance stored in a field of MyModule. However now the provider has its own dependencies - hence I need to define a non-anonymous class I specify an @Inject constructor on. Something like:

public class MyModule extends AbstractModule {
  private static final Foo foo;

  public MyModule(Foo foo) {
    this.foo = foo;
  }

  @Override
  public void configure() {
    bind(Bar.class).toProvider(BarProvider.class);
  }

  BarProvider implements Provider<Bar> {
    private Baz baz;

    @Inject BarProvider(Baz baz) {
      this.baz = baz;
    }

    @Override public Bar get() {
      return foo.getBar(baz);
    }
  }
}

Perfect! Except Guice doesn't like this...

Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Injecting into inner classes is not supported. Please use a 'static' class (top-level or nested) instead of com.example.MyModule$BarProvider.

So, I'm in a bind. I need to access both a field on the module and an injected type from a Provider class at the same time. Is there any way to do this?


Note: this toy example excludes some of the actual complexity - in particular the bind() statement is more involved, which is why I can't simply define an @Provides method.

dimo414
  • 47,227
  • 18
  • 148
  • 244

2 Answers2

2

In part, injecting into an inner class is impossible because Guice can't reflectively create an inner instance without an outer parent instance (the equivalent of the arcane outerInstance.new InnerInstance() syntax).

Some options:

  • Make Foo injectable through your graph, possibly hidden in a PrivateModule so it's not exposed to your entire graph (if that's important to you).
  • Use an anonymous inner Provider (or an extracted equivalent), and get a Provider<Baz> from AbstractModule's getProvider(Class<T>) method. You'll get an exception if you try to call that before the Injector is created, but for creating a Provider the way you're doing, that's probably not a problem.
  • Post your bind outside of a toy problem, to see whether @Provides is possible with some cleverness.

Related: Accessing Guice injector in its Module?

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thanks Jeff! I realized after posting that what I needed was `getProvider()`. It was staring me right in the face, but for some reason it didn't click :) – dimo414 Feb 03 '17 at 07:00
0

I realize I was getting hung up on having Guice construct my Provider for me, which I don't actually need to do. Despite the example in Guice's documentation passing in a DatabaseTransactionLogProvider.class a better parallel to the first snippet would be to construct an instance of my Provider manually, and pass in both the Foo instance and a Provider<Baz> instance (provided by the module).

public class MyModule extends AbstractModule {
  private static final Foo foo;

  public MyModule(Foo foo) {
    this.foo = foo;
  }

  @Override
  public void configure() {
    bind(Bar.class).toProvider(new BarProvider(foo, getProvider(Baz.class));
  }

  static BarProvider implements Provider<Bar> {
    private final Foo foo;
    private final Provider<Baz> bazProvider;

    BarProvider(Foo foo, Provider<Baz> bazProvider) {
      this.foo = foo;
      this.bazProvider = bazProvider;
    }

    @Override public Bar get() {
      return foo.getBar(bazProvider.get());
    }
  }
}
dimo414
  • 47,227
  • 18
  • 148
  • 244