2010-11-21 2 views
41

Guiceを使用してジェネリックインタフェースのジェネリックインプリメンテーションをインジェクトでき​​るようにしたいと考えています。 Castle.Windsorを使ってC#でGuiceを使用してジェネリックインプリメンテーションを実行する

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

、私はto doことができるだろう:

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

が、私は相当Guiceの中に存在しているとは思いません。私はGuiceのTypeLiteralを個々の実装を登録するのに使うことができると知っていますが、Windsorのようにそれらを一度に登録する方法はありますか?

編集:

ここでの使用例です:

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

可能性が高く使用量が別のクラスへの注入になりますが:でジェネリックを使用するためには

public class ClassThatUsesRepository { 
    private Repository<Class1> repository; 

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

あなたはどのようにあなたが希望を示すスニペットを追加することができます_use_これは好きですか? –

+2

私はあなたと一緒にいる、私は同じことをしたい。誰もがこの問題を抱えているはずです。彼らが私たちに言っていないものがなければならない。 :) – PapaFreud

+0

私は解決策も知っていますが、私はC#については何も知っていませんが、明らかにC#の方がずっと現代的です。 – Mike

答えて

51

Guiceでは、TypeLiteralクラスを使用して汎用バリアントをバインドする必要があります。

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

(リポジトリは一般的なインタフェースで、MyRepositoryは一般的な実装で、Class1のは、ジェネリック医薬品に使用される特定のクラスである):これはGuiceのインジェクタの設定は次のようになります方法の例です。

+6

これが私のやり方です。私が望んでいたことは、個々の実装(MyRepository 、MyRepository など)を登録する必要性を排除することです。それがウィンザーの例です。 –

+2

申し訳ありませんが、私はあなたの質問をより慎重に読むべきでした。私はそのタイプのGuiceジェネリック医薬品の使用法を調べてきましたが、私はそれを解決することもできませんでした。私はそれを解決する一つの方法はGuiceを拡張し、独自のモジュール(ヘルパー)を書くことだろうと思います。 JavaリフレクションAPIを使用すると、すべてのInjectionバリアントを見つけてバインドすることができます。 – Kdeveloper

3

ジェネリックは実行時に保持されないので、最初はコンセプトを把握するのが難しくなりました。 とにかく、new ArrayList<String>().getClass()Class<?>ではなくClass<String>ではありませんが、それをClass<? extends String>にキャストすることは安全ですが、コンパイル時の型チェック(暗黙的な検証のようなものです)があることを忘れないでください。

したがって、Repository(任意のタイプ)の新しいインスタンスが必要な場合はいつでも、Guiceを使用してMyRepository(任意のタイプ)実装を注入したい場合は、ジェネリックについて考える必要はありません型の安全性を確保するためにあなた自身で行うことができます(そのため、これらの厄介な "未チェック"警告が発生します)。

ここ

がうまく動作するコードの例を次に示します。

I'm a String 
33 

をしかし、そのリストLinkedListによって実装です:

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

これが印刷されます。この例では何かをの文字列に設定しようとすると例外が発生します。

int i = collection.get(0) 

しかし、あなたは、注射のオブジェクトを取得したい場合は、既にキャスト型、あなただけではなく、リストのList<String>を求めることができダンディが、その後、Guiceのがと同様の結合キー(の一部として、その型変数を扱います@Namedなどの修飾子)。つまり、具体的にはList<String>ArrayList<String>の実装に、List<Integer>LinkedList<Integer>に注入したい場合、Guiceはこれを行うことができます(テストされていない、推測されていない推測)。

しかし、落とし穴があります:

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

あなたはクラスリテラルは汎用的ではないに気づくかもしれませんが。それはGuiceのTypeLiteralsを使用する場所です。

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

TypeLiterals所望の実装にマッピングするメタ情報の一部として、ジェネリック型変数を保持します。お役に立てれば。

0

あなたは(虐待?)を使用することができます@ImplementedBy注釈を作るためにGuiceのあなたのための汎用的なバインディングを生成:

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

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

をジャストインタイムバインディングが有効になっている限り、あなたは明示的な結合することなくRepository<Whatever>を注入することができます:

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

キャッチが結合のターゲットはMyRepositoryはなく、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]]} 

これは通常問題ではありませんが、MyRepositoryTypeLiteral<T>を注入して実行時に独自の型を特定することはできません。これはこの状況で特に便利です。それを除いて、私の知る限り、これはうまく動作します。

(誰かがこれを固定のように感じる場合は、私はそれだけでソースキーからターゲット型パラメータを埋めるためにいくつかの余分な計算around hereが必要になりますかなり確信しています。)