2010-11-30 19 views
8

¿Qué hago con Guice cuando necesito llamar a un constructor principal que también es inyectable? p.ej. Tengo una clase principal abstracta que tiene un constructor que se inyecta con un objeto compartido por todos los niños derivados y cada niño también tiene un constructor inyectable.Guice con los padres

Llamar a super() no funcionará porque Java quiere que pase el objeto como un parámetro en vez de hacer que Guice se inyecte.

Gracias

EDIT: Me pregunto si tal vez necesito usar la inyección método en su lugar?

Respuesta

9

Tendrá que hacer exactamente lo mismo que usted si no estuviera usando Guice ... declare también los parámetros que el constructor padre requiere como parámetros al constructor de cada niño, y páselos a super.

lo tanto, si el constructor de su clase padre abstracta toma un Foo, el constructor de una clase hija tiene que quedar así:

@Inject public ChildClass(Foo foo, Bar bar) { 
    super(foo); 
    this.bar = bar; 
    ... 
} 
+0

¿Qué sucede si más adelante en el código necesito construir ChildClass? ¿Cuál es la mejor manera de hacer eso? Al igual que ChildClass child = new ChildClass (nuevo Foo(), barra)? ¿Qué pasa si Foo también tiene inyecciones? – lapkritinis

4

enterrado en la sección Minimize Mutability de las mejores prácticas Guice, encontrará esta directriz:

Las subclases deben llamar al super() con todas las dependencias. Esto hace que el constructor sea engorroso, especialmente porque la clase de base inyectada cambia.

En la práctica, aquí está cómo hacerlo mediante la inyección de constructor:

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

Una mejor alternativa es usar algo similar al patrón de la estrategia para encapsular todos los campos de la superclase quiere inyectar, y luego la subclase puede inyectar eso. Por ejemplo:

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

    ... 
}