Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice injection null pointer

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...

like image 845
user1810567 Avatar asked Nov 08 '12 21:11

user1810567


People also ask

Can Guice inject null?

Guice forbids null by default So if something tries to supply null for an object, Guice will refuse to inject it and throw a NULL_INJECTED_INTO_NON_NULLABLE ProvisionException error instead. If null is permissible by your class, you can annotate the field or parameter with @Nullable .

What does @inject do Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

What is the use of @inject annotation?

Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.

How do you inject a Guice class?

Client Application We need to create Injector object using Guice class createInjector() method where we pass our injector class implementation object. Then we use injector to initialize our consumer class. If we run above class, it will produce following output.


2 Answers

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.

like image 81
Jeff Bowman Avatar answered Sep 28 '22 22:09

Jeff Bowman


I assume that your are not creating your AboutDialog with the help of Guice.

What you could do is use injector.injectMembers(this) where this is the AboutDialog.

The best way would be that the AboutDialog will be created by Guice, so all members will be injected.

like image 21
Daniel Manzke Avatar answered Sep 28 '22 22:09

Daniel Manzke