2010-11-21 97 views
41

我希望能夠使用Guice注入通用接口的通用實現。使用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 
    } 
} 

在使用Castle.Windsor C#,我就能to do

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

,但我不認爲相當於吉斯存在。我知道我可以在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

您可以加入一個片段顯示你將如何喜歡用這個? – 2010-11-21 16:51:59

+2

我和你在一起,我想要做同樣的事情。每個人都應該有這個問題。一定有一些他們沒有告訴我們的東西。 :) – PapaFreud 2011-03-22 10:18:44

+0

我也想知道解決方案,我對C#一無所知,但顯然C#方式更現代化。 – Mike 2012-03-01 13:02:35

回答

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

(Repository是通用接口,MyRepository是通用的實現,Class1的是仿製藥使用的特定類)。

+6

這就是我如何去做的。我希望做的是不需要註冊每個單獨的實現(MyRepository ,MyRepository 等)。這就是溫莎的例子。 – 2010-11-21 20:21:37

+2

對不起,我應該更仔細地閱讀你的問題。我一直在研究這種類型的Guice泛型,但我無法解決它。我想解決這個問題的一種方法是擴展Guice並編寫你自己的模塊(幫助程序)。通過使用Java反射API,您可以找到所有注入變體並將其綁定。 – Kdeveloper 2010-11-22 00:18:53

3

在運行時沒有保留泛型,一開始就很難理解概念。 無論如何,有理由new ArrayList<String>().getClass()返回Class<?>而不是Class<String>儘管它可以安全地將它轉換爲Class<? extends String>但您應該記住泛型僅用於編譯時類型檢查(有點像隱式驗證,如果您願意的話)。所以如果你想在任何需要Repository(任何類型)的新實例時使用Guice來注入MyRepository(與任何類型)的實現,那麼你根本不必考慮泛型,但是你以確保類型安全(這就是爲什麼你得到那些討厭的「未檢查」警告)。

這裏的代碼工作得很好的例子:

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

此打印:

I'm a String 
33 

但該名單LinkedList實現。雖然在這個例子中,如果你試圖設計一個int字符串你會得到一個異常。

int i = collection.get(0) 

但是,如果你想獲得已型鑄造的注射對象和花花公子你可以要求List<String>,而不是僅僅名單,但隨後吉斯將把該型變量作爲綁定密鑰的一部分(類似限定符如@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註釋,使吉斯生成通用綁定你:

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

這通常不是問題,但這意味着MyRepository不能注入TypeLiteral<T>以在運行時找出它自己的類型,這在這種情況下特別有用。除此之外,據我所知,這工作正常。

(如果有人感覺就像修復此,我敢肯定,它只是需要一些額外的計算around here填補從源頭關鍵目標類型的參數。)