2010-11-21 122 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填补从源头关键目标类型的参数。)