Guice injection null pointer

user1810567 picture user1810567 · Nov 8, 2012 · Viewed 18.2k times · Source

We try to refactoring a project with Guice. The idea is to bind all the Language interface to a concreate object like French or Polish.

We have a module for binding:

public class StandardModule extends AbstractModule {

    @Override
    protected void configure() {

       bind(Language.class).to(Polish.class);

    }
 }

And a classe (AboutDialog.java) that use this injected object :

@Inject Language language;

public AboutDialog(JFrame parent) {
    super(parent, "", true);
    this.language=language;
    this.setTitle(language.getLanguageInUse().getString("AboutDialog.title"));
    this.parent = parent;
    try {
        jbInit();
    } catch (Exception e) {
        e.printStackTrace();
    }
    pack();
}

And we have as result:

java.lang.NullPointerException at net.sf.jmoney.gui.AboutDialog.<init>(AboutDialog.java:67)

Line 67 is:

this.setTitle(language.getLanguageInUse().getString("AboutDialog.title"));

Our interface is:

public interface Language {

    public ResourceBundle getLanguageInUse();
}

And the Polish class is:

public class Polish implements Language {

    private ResourceBundle languageInUse;

    public Polish() {
        languageInUse = ResourceBundle.getBundle(Constants.LANGUAGE_PL);
    }

    public ResourceBundle getLanguageInUse() {
        return languageInUse;
    }


}

We are lost...

Answer

Jeff Bowman picture Jeff Bowman · Nov 9, 2012

You're using "field injection". This will make it hard to use your injected values in a constructor; even if Guice were to create the object (which is not happening now) or you were to use injector.injectMembers(aboutDialog), the constructor would run before the injector has a chance to inject the field you want.

It's a little more tricky to create a class that takes a varying parameter as well as an injected parameter. This leaves you with a few options:

  • Inject the JFrame. If you know what JFrame you're going to use when the constructor is being created, then just use bind(JFrame.class).toInstance(myJFrame); in your Module. Then Guice can create the AboutDialog entirely.

  • Create a Factory manually. That way you can inject AboutDialog.Factory and just call create to get your AboutDialog. It'll look something like this:

    public class AboutDialog extends JDialog {
    
      /** Injectable factory. */
      public static class Factory {
        @Inject private Language language;
    
        public AboutDialog create(JFrame parent) {
          return new AboutDialog(parent, language);
        }
      }
    
      // no @Inject parameter; you're calling "new" yourself above!
      public AboutDialog(JFrame parent, Language language) {
        super(parent, "", true);
        this.language = language;
        // ... other initialization
      }
    }
    
  • Create a Factory and let Guice wire it up for you via assisted injection.

    public class AboutDialog extends JDialog {
    
      public interface Factory {
        public AboutDialog create(JFrame parent);
      }
    
      // you need the @Inject, and also the @Assisted to tell Guice to
      // use the parameter instead of Guice bindings
      @Inject
      public AboutDialog(@Assisted JFrame parent, Language language) {
        super(parent, "", true);
        this.language = language;
        // ... other initialization
      }
    }
    
    public class StandardModule extends AbstractModule {
      @Override protected void configure() {
        bind(Language.class).to(Polish.class);
    
        // here every method in AboutDialog.Factory will be implemented
        // to create the method's return type [AboutDialog] based on
        // the parameters (like JFrame) and the bindings (like Language)
        install(new FactoryModuleBuilder().build(AboutDialog.Factory.class));
      }
    }
    

As noted in the question comments, make sure you're getting your AboutDialog (or AboutDialog.Factory via an @Injected constructor/field or from the Injector itself, or else Guice will not know to inject the parameters.