2010-11-21 25 views
41

Ich möchte in der Lage sein, eine generische Implementierung einer generischen Schnittstelle mit Guice injizieren.Injizieren Generische Implementierung mit Guice

Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>)) 

aber ich glaube nicht das Äquivalent in Guice existiert:

public interface Repository<T> { 
    void save(T item); 
    T get(int id); 
} 

public MyRepository<T> implements Repository<T> { 
    @Override 
    public void save(T item) { 
    // do saving 
    return item; 
    } 
    @Override 
    public T get(int id) { 
    // get item and return 
    } 
} 

In C# Castle.Windsor verwenden, würde ich in der Lage to do sein. Ich weiß, dass ich in Guice TypeLiteral verwenden kann, um einzelne Implementierungen zu registrieren, aber gibt es irgendeine Möglichkeit, sie alle auf einmal wie in Windsor zu registrieren?

Edit:

Hier ist ein Beispiel für die Nutzung:

Injector injector = Guice.createInjector(new MyModule()); 
Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {}); 
Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {}); 

Obwohl desto wahrscheinlicher Nutzung würde die Injektion in eine andere Klasse sein:

public class ClassThatUsesRepository { 
    private Repository<Class1> repository; 

    @Inject 
    public ClassThatUsesRepository(Repository<Class1> repository) { 
    this.repository = repository; 
    } 
} 
+0

Könnten Sie zeigt einen Ausschnitt hinzufügen Wie würden Sie gerne _use_ das? –

+2

Ich bin bei dir, ich möchte das Gleiche machen. Jeder sollte dieses Problem haben. Es muss etwas geben, was sie uns nicht sagen. :) – PapaFreud

+0

Ich würde gerne die Lösung auch wissen, ich weiß nichts über C#, aber offensichtlich ist die C# -Wege viel moderner. – Mike

Antwort

51

Um Generika zu verwenden, um mit Guice müssen Sie die Klasse TypeLiteral verwenden, um die generischen Varianten zu binden. Dies ist ein Beispiel dafür, wie Sie Guice Injektor-Konfiguration sind aussehen könnte:

package your-application.com; 

import com.google.inject.AbstractModule; 
import com.google.inject.TypeLiteral; 

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    bind(new TypeLiteral<Repository<Class1>>(){}) 
     .to(new TypeLiteral<MyRepository<Class1>>(){}); 
    } 
} 

(Repository die generische Schnittstelle ist, MyRepository ist die generische Implementierung ist Class1 die spezifische Klasse in dem Generika verwendet).

+6

So mache ich es. Ich wollte damit verhindern, dass jede einzelne Implementierung registriert werden muss (MyRepository , MyRepository , etc.). Das ist das Beispiel von Windsor. –

+2

Tut mir leid, ich hätte deine Frage besser lesen sollen. Ich habe mich mit dieser Art von Guice Generika beschäftigt, aber ich konnte es auch nicht lösen. Ich denke, ein Weg, es zu lösen, wäre, Guice zu erweitern und ein eigenes Modul (Helfer) zu schreiben. Mit der Java Reflection API können Sie alle Injection Varianten finden und binden. – Kdeveloper

3

Generics, die zur Laufzeit nicht beibehalten werden, machten es zunächst schwieriger, das Konzept zu verstehen. Anyways, gibt es Gründe new ArrayList<String>().getClass() gibt Class<?> zurück und nicht Class<String> und obwohl es sicher ist, es auf Class<? extends String> zu werfen, sollten Sie sich daran erinnern, dass Generika nur für Kompilierzeit Typüberprüfungen (Art der impliziten Validierung, wenn Sie so sind).

Also, wenn Sie wollen Guice verwenden MyRepository (mit jeder Art) Implementierung zu injizieren, wenn Sie eine neue Instanz von Repository (mit jeder Art) müssen dann haben Sie gar nicht über Generika zu denken, aber du bist auf eigene Faust, um die Typensicherheit zu gewährleisten (deshalb erhalten Sie diese lästige "ungeprüfte" Warnung). Hier

ist ein Beispiel für Code funktioniert ganz gut:

public class GuiceTest extends AbstractModule { 

    @Inject 
    List collection; 

    public static void main(String[] args) { 
     GuiceTest app = new GuiceTest(); 
     app.test(); 
    } 

    public void test(){ 
     Injector injector = Guice.createInjector(new GuiceTest()); 
     injector.injectMembers(this); 

     List<String> strCollection = collection; 
     strCollection.add("I'm a String"); 
     System.out.println(collection.get(0)); 

     List<Integer> intCollection = collection; 
     intCollection.add(new Integer(33)); 
     System.out.println(collection.get(1)); 
    } 

    @Override 
    protected void configure() { 
     bind(List.class).to(LinkedList.class); 
    } 
} 

Diese Drucke:

I'm a String 
33 

Aber diese Liste von einem LinkedList implementiert ist. Obwohl in diesem Beispiel, wenn Sie versuchten, eine Int etwas, das String ist, würden Sie eine Ausnahme erhalten.

int i = collection.get(0) 

Aber wenn Sie bereits ein injizierbares Objekt erhalten typ gegossen und Dandy Sie List<String> statt nur Liste fragen können, aber dann wird Guice, dass Typ-Variable als Teil des Bindungsschlüssels (ähnlich eine Behandlung Qualifier wie @Named).Was das bedeutet ist, dass, wenn Sie speziell List<String> von ArrayList<String> Implementierung und List<Integer> von LinkedList<Integer> sein wollen, Guice können Sie das tun (nicht getestet, erraten).

Aber es gibt einen Haken:

@Override 
    protected void configure() { 
     bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening* 
    } 

Wie Sie Klasse Literale bemerken könnten, sind nicht generisch. Dort verwenden Sie Guices TypeLiterals.

@Override 
    protected void configure() { 
     bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){}); 
    } 

TypeLiterals der gattungs Variable als Teil der Meta-Informationen behalten, um die gewünschte Umsetzung zu kartieren. Hoffe das hilft.

0

können Sie (Missbrauch?) Verwenden, um die @ImplementedBy Anmerkung Guice erzeugen generic Bindungen für Sie zu machen:

@ImplementedBy(MyRepository.class) 
interface Repository<T> { ... } 

class MyRepository<T> implements Repository<T> { ... } 

Solange just-in-time-Bindungen aktiviert sind, können Sie Repository<Whatever> ohne explizite Bindung injizieren :

Injector injector = Guice.createInjector(); 
    System.out.println(injector.getBinding(new Key<Repository<String>>(){})); 
    System.out.println(injector.getBinding(new Key<Repository<Integer>>(){})); 

der Haken ist, dass das Ziel der Bindung MyRepository, anstatt MyRepository<T>:

LinkedKeyBinding{key=Key[type=Repository<java.lang.String>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} 
LinkedKeyBinding{key=Key[type=Repository<java.lang.Integer>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} 

Das ist normalerweise kein Problem, aber es bedeutet, dass MyRepository keine TypeLiteral<T> injizieren kann, um seinen eigenen Typ zur Laufzeit herauszufinden, was in dieser Situation besonders nützlich wäre. Abgesehen davon funktioniert das nach meinem besten Wissen gut.

(Wenn jemand wie Festsetzung dieses glaubt, ich bin ziemlich sicher, dass es nur einige zusätzliche Berechnungen around here erfordern würde, in den Zieltyp Parameter aus der Quellenschlüssel zu füllen.)

Verwandte Themen