2010-09-25 15 views
12

El siguiente código es un ejemplo de una fábrica que produce un Bar<T> dado un Foo<T>. A la fábrica no le importa qué es T: para cualquier tipo T, puede hacer un Bar<T> desde un Foo<T>.Inyección de una fábrica genérica en Guice

import com.google.inject.*; 
import com.google.inject.assistedinject.*; 

class Foo<T> { 
    public void flip(T x) { System.out.println("flip: " + x); } 
} 

interface Bar<T> { 
    void flipflop(T x); 
} 

class BarImpl<T> implements Bar<T> { 
    Foo<T> foo; 

    @Inject 
    BarImpl(Foo<T> foo) { this.foo = foo; } 

    public void flipflop(T x) { foo.flip(x); System.out.println("flop: " + x); } 
} 

interface BarFactory { 
    <T> Bar<T> create(Foo<T> f); 
} 

class Module extends AbstractModule { 
    public void configure() { 
    bind(BarFactory.class) 
     .toProvider( 
      FactoryProvider.newFactory(BarFactory.class, BarImpl.class) 
        ); 
    } 
} 

public class GenericInject { 
    public static void main(String[] args) { 
    Injector injector = Guice.createInjector(new Module()); 

    Foo<Integer> foo = new Foo<Integer>(); 
    Bar<Integer> bar = injector.getInstance(BarFactory.class).create(foo); 
    bar.flipflop(0); 
    } 
} 

Cuando ejecuto el código, me sale el siguiente error de Guice:

1) No implementation for BarFactory was bound. 
    at Module.configure(GenericInject.java:38) 

2) Bar<T> cannot be used as a key; It is not fully specified. 

La única referencia que puedo encontrar a los genéricos en la documentación Guice dice utilizar un TypeLiteral. Pero no tengo un tipo literal, tengo un marcador de posición genérico que no es relevante para la fábrica en absoluto. ¿Algun consejo?

Respuesta

-1

Si piensas en Guice como un sistema de cableado similar a la primavera, entonces no tiene sentido cablear una instancia genérica. Está conectando instancias específicas a las claves, de modo que cuando otra clase instanciada marque algo con @Inject BarFactory obtenga la instancia creada específica.

Dado que su implementación es genérica, no ha proporcionado suficiente información para inyectar una instancia específica. Aunque no he utilizado factoryprovider, supongo que deberá vincular el Barfactory a una instancia totalmente parametrizada, p. BarImpl<Concrete> en lugar de BarImpl)

Dicho sea de paso, ya que está vinculando BarFactory.class si desea enlazar varias instancias, tendrá que inflexionarlas de alguna manera, ya sea por nombre, algo así como (no se ha verificado la sintaxis, pero)

bind(BarFactory.class).annotatedWith(Names.named("name1")) 
     .toProvider( 

or by generics, bind(BarFactory<Concrete>).toProvider... 
+1

BarFactory no es genérico, por lo que tiene mucho sentido cablearlo como lo he cableado. Una instancia parametrizada de Bar no coincidiría con el contrato: crear necesita tomar un Foo y devolver una barra para cualquier T. –

4

Una opción es simplemente escribir el texto modelo BarFactory a mano:

class BarImplFactory implements BarFactory { 
    public <T> Bar<T> create(Foo<T> f) { 
    return new BarImpl(f); 
    } 
} 

la unión se convierte en

bind(BarFactory.class).to(BarImplFactory.class); 
+0

Will Guice inyectará los demás parámetros de constructor 'BarImpl' incluso si su constructor está siendo llamado explícitamente sin Guice? –

+1

No, no lo creo. Aún necesitarás proporcionar tus propios Foos. –

+2

@Jeff Axelrod, puede inyectar los otros parámetros de constructor de BarImpl inyectando primero al constructor de BarImplFactory, y luego copiando esos valores. – Shuo

Cuestiones relacionadas