我希望能够使用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中存在等价物。我知道我可以在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 :(得分:61)
为了在Guice中使用泛型,您需要使用TypeLiteral类来绑定泛型变体。这是Guice进样器配置的示例:
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是泛型中使用的特定类)。
答案 1 :(得分:5)
在运行时没有保留的泛型确实使得最初掌握这个概念变得更加困难。
无论如何,有new ArrayList<String>().getClass()
返回Class<?>
而不是Class<String>
的原因虽然可以安全地将其转换为Class<? extends String>
,但您应该记住,泛型只适用于编译时类型检查(有点像隐式验证,如果你愿意的话)。
因此,如果您想在需要MyRepository
(任何类型)的新实例时使用Guice注入Repository
(任何类型)实现,那么您不必考虑关于仿制药,但你自己确保类型安全(这就是为什么你得到那些讨厌的&#34;未经检查&#34;警告)。
这是一个代码工作正常的例子:
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>
而不仅仅是List,但是然后Guice会将该Type变量视为绑定键的一部分(类似于限定符,如@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
将泛型类型变量保留为元信息的一部分,以映射到所需的实现。希望这会有所帮助。
答案 2 :(得分: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]]}
这通常不是问题,但这意味着MyRepository
无法在运行时注入TypeLiteral<T>
来计算自己的类型,这在这种情况下尤其有用。除此之外,据我所知,这很好用。
(如果有人想要修复此问题,我很确定只需要一些额外的计算around here来填充源密钥中的目标类型参数。)
答案 3 :(得分:0)
有点相关,希望有人会觉得这很有用。在某些情况下,尤其是当您具有要泛化的类型的java.lang.Class实例时,可以通过扩展ParameterizedType类来强制在运行时进行注入。
在下面的解决方案中,工厂方法创建一个通用集合&lt; ? extends Number&gt; 和 Map&lt; K,V&gt; 给定类对象的实例
<强> Example.java:强>
@SuppressWarnings("unchecked")
public class Example<K extends Number> {
Injector injector = ...
public Set<K> foo(Class<K> klass) {
CompositeType typeLiteral = new CompositeType(Set.class, klass);
Set<K> set = (Set<K>) injector.getInstance(Key.get(typeLiteral));
return set;
}
public <V> Map<K,V> bar(Class<K> keyClass, Class<V> valueClass) {
CompositeType typeLiteral = new CompositeType(Map.class, keyClass, valueClass);
Map<K,V> collection = (Map<K,V>) injector.getInstance(Key.get(typeLiteral));
return collection;
}
}
<强> CompositeType.java:强>
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
public class CompositeType implements ParameterizedType {
private final String typeName;
private final Class<?> baseClass;
private final Type[] genericClass;
public CompositeType(Class<?> baseClass, Class<?>... genericClasses) {
this.baseClass = baseClass;
this.genericClass = genericClasses;
List<String> generics = ((List<Class<?>>)Arrays.asList(genericClasses))
.stream()
.map(Class::getName)
.collect(Collectors.toList());
String genericTypeString = StringUtils.join(generics, ",");
this.typeName = baseClass.getName() + "<" + genericTypeString + ">";
}
@Override
public String getTypeName() {
return typeName;
}
@Override
public Type[] getActualTypeArguments() {
return genericClass;
}
@Override
public Type getRawType() {
return baseClass;
}
@Override
public Type getOwnerType() {
return null;
}
}