2010-09-25 3 views
12

Le code suivant est un exemple d'usine qui produit un Bar<T> avec un Foo<T>. L'usine ne se soucie pas de ce que T est: pour tout type T, il peut faire un Bar<T> à partir d'un Foo<T>.Injection d'une fabrique générique dans 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); 
    } 
} 

Quand je lance le code, je reçois les erreurs suivantes 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 seule référence que je peux trouver aux médicaments génériques dans la documentation Guice dit d'utiliser un TypeLiteral. Mais je n'ai pas de type littéral, j'ai un espace réservé générique qui n'est pas du tout pertinent pour l'usine. Des conseils?

Répondre

-1

Si vous considérez que guice est un système de câblage similaire à celui du printemps, cela n'a pas vraiment de sens de câbler une instance générique. Vous installez des instances spécifiques sur des clés, de sorte que lorsqu'une autre classe instanciée marque quelque chose avec @Inject BarFactory, vous obtenez l'instance créée spécifique.

Étant donné que votre implémentation est générique, vous n'avez pas fourni suffisamment d'informations pour injecter une instance spécifique. Bien que je n'ai pas utilisé factoryprovider, mon hypothèse est que vous devrez lier la Barfactory à une instance entièrement paramétrée, par ex. Par ailleurs, puisque vous liez BarFactory.class si vous voulez lier plusieurs instances, vous devrez les infléchir d'une manière ou d'une autre, soit par nom, soit par quelque chose comme (n'a pas vérifié la syntaxe, mais)

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

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

BarFactory n'est pas générique, il est donc logique de le câbler comme je l'ai câblé. Une instance paramétrée de Bar ne correspondrait pas au contrat: create doit prendre un Foo et retourner une Barre pour tout T. –

4

Une option consiste à écrire juste le passe-partout BarFactory main:

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

la liaison devient

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

Est-ce que Guice injectera les autres paramètres du constructeur de BarImpl même si son constructeur est appelé explicitement sans Guice? –

+1

Non, je ne pense pas. Vous devrez toujours fournir vos propres Foos. –

+2

@Jeff Axelrod, Vous pouvez injecter les autres paramètres constructeur de BarImpl en les injectant d'abord dans le constructeur de BarImplFactory, puis en copiant ces valeurs. – Shuo