Guice with parents

蹲街弑〆低调 提交于 2019-11-30 23:53:36

问题


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?


回答1:


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



回答2:


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);
   }
}



回答3:


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;
  }

  ...
}


来源:https://stackoverflow.com/questions/4309450/guice-with-parents

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!