Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice with parents

Tags:

java

guice

What do I do with Guice when I need to call a parent constructor that is also injectable? e.g. I have an abstract parent class that has a constructor that is injected with an object shared by all derived children and each child also has an injectable constructor.

Calling super() wont work because Java wants me to pass in the object as a paremeter rather than have Guice inject.

Thanks

EDIT: I am wondering if maybe I need to use method injection instead?

like image 521
christophmccann Avatar asked Nov 30 '10 00:11

christophmccann


3 Answers

A better alternative is to use something similar to the strategy pattern to encapsulate all the fields the superclass wants to inject, and then the subclass can inject that. For example:

public abstract class Animal {
  /**
   * All injectable fields of the Animal class, collected together
   * for convenience.
   */
  protected static final class AnimalFields {
    @Inject private Foo foo;
    @Inject private Bar bar;
  }

  private final AnimalFields fields;

  /** Protected constructor, invoked by subclasses. */
  protected Animal(AnimalFields fields) {
    this.fields = fields;
  }

  public Foo getFoo() {
    // Within Animal, we just use fields of the AnimalFields class directly
    // rather than having those fields as local fields of Animal.
    return fields.foo;
  }

  public Bar getBar() {
    return fields.bar;
  }
}

public final class Cat extends Animal {
  private final Whiskers whiskers;

  // Cat's constructor needs to inject AnimalFields to pass to its superclass,
  // but it can also inject whatever additional things it needs.
  @Inject
  Cat(AnimalFields fields, Whiskers whiskers) {
    super(fields);
    this.whiskers = whiskers;
  }

  ...
}
like image 93
Daniel Pryden Avatar answered Oct 06 '22 07:10

Daniel Pryden


You'd need to do the exact same thing you do if you weren't using Guice... declare any parameters the parent constructor requires as parameters to each child's constructor as well, and pass those to super.

So if your abstract parent class's constructor takes a Foo, a child class's constructor needs to look like:

@Inject public ChildClass(Foo foo, Bar bar) {
  super(foo);
  this.bar = bar;
  ...
}
like image 43
ColinD Avatar answered Oct 06 '22 06:10

ColinD


Buried in the Minimize Mutability section of the Guice Best Practices, you'll find this guideline:

Subclasses must call super() with all dependencies. This makes constructor injection cumbersome, especially as the injected base class changes.

In practice, here's how to do it using constructor injection:

public class TestInheritanceBinding {
   static class Book {
      final String title;
      @Inject Book(@Named("GeneralTitle") String title) {
         this.title = title;
      }
   }
   static class ChildrensBook extends Book {
      @Inject ChildrensBook(@Named("ChildrensTitle") String title) {
         super(title);
      }
   }
   static class ScienceBook extends Book {
      @Inject ScienceBook(@Named("ScienceTitle") String title) {
         super(title);
      }
   }

   @Test
   public void bindingWorked() {
      Injector injector = Guice.createInjector(new AbstractModule() {
         @Override protected void configure() {
            bind(String.class).
            annotatedWith(Names.named("GeneralTitle")).
            toInstance("To Kill a Mockingbird");
            bind(String.class).
            annotatedWith(Names.named("ChildrensTitle")).
            toInstance("Alice in Wonderland");
            bind(String.class).
            annotatedWith(Names.named("ScienceTitle")).
            toInstance("On the Origin of Species");
         }
      });
      Book generalBook = injector.getInstance(Book.class);
      assertEquals("To Kill a Mockingbird", generalBook.title);
      ChildrensBook childrensBook = injector.getInstance(ChildrensBook.class);
      assertEquals("Alice in Wonderland", childrensBook.title);
      ScienceBook scienceBook = injector.getInstance(ScienceBook.class);
      assertEquals("On the Origin of Species", scienceBook.title);
   }
}
like image 42
Jeff Axelrod Avatar answered Oct 06 '22 07:10

Jeff Axelrod