[英]Java get collection by generic type
假设我有类似这样的类Context
。
public class Context {
Set<A> sA;
Set<B> sB;
Set<C> sC;
}
然后我有一个泛型类Performer
,它希望基于Context
类中的泛型类型执行操作,方法如下 - 在Java中不可能。
public class Performer<T> { // T can be type of A,B or C
public void saveToSet(T t) {
Context.Set<T>.add(t);
}
}
然后,如果我说类似saveToSet(B b)
,将调用Context
的右集,在本例中为sB (Set<B>)
,新实例将被添加到该集合中。
好的,问题......在Java中怎么做? :d
遗憾的是,由于类型擦除,这种方法在Java中不起作用。
基本上所有运行时都会看到Context.Set<java.lang.Object>.add(t);
并且你的字段崩溃为java.lang.Object
的Set
,因此它将无法消除歧义。
您可以通过编写saveToSet(A a)
, saveToSet(B b)
等重载来解决此问题。
在很多方面,包括这个,Java泛型是C ++模板的穷表兄弟。
//Not very ice, but something like this can be useful
if (x instanceof A) {
sA.add((A)x);
return;
}
if (x instanceof B) {
sB.add((B)x);
return;
}
由于Java中称为类型擦除的概念,这将无法工作。 基本上,泛型类型在运行时被擦除,并且运行时将不知道它是哪种类型的对象。 所以不,你不能做那样的事情。
但是,您可以创建单独的方法,或者可以在该重写方法中创建继承的类并实现特定的逻辑。
Java的一种解决方法是使用丰富的类型。 这些基本上是将自己的通用参数保存为Class<T>
属性的类型。 因此,使用您自己的存储类的实现,而不是使用标准Set
。 就像是:
class TypeStoringSet<T> extends HashSet<T> {
private Class<T> clazz;
... getter and setter here
public TypeStoringSet<T>(Class<T> clazz) {
super();
this.clazz = clazz;
}
}
也许你可以使用这种方法。
class Context {
Map<String,Set<Object>> datasets = new HashMap<>();
public <T> void add(T data){
final String name = data.getClass().getName();
if(!datasets.containsKey(name)) datasets.put(name, new HashSet<>());
datasets.get(name).add(data);
}
public <T> Set<T> get(Class<T> type){
return (Set<T>) datasets.get(type.getName());
}
}
class Performer<T> { // T can be ANY type
public void saveToSet(T t) {
final Context context = new Context();
context.add(t);
}
}
final Set<A> aSet = context.get(A.class);
final Set<B> bSet = context.get(B.class);
final Set<C> cSet = context.get(C.class);
其他可能的方法是使用Guice命名注入和方法拦截(减去:低性能)并且需要对此代码进行大量优化(类类型处理,可能是从Guice MapPultibinding到注入datasets
)
实际上,您可以使用反射实现目标并绕过类型擦除。 但前提是您的Context
具有明确定义的泛型类型参数的字段。
因此,如果你不害怕性能损失,那么反思就是你的朋友。 您可以像这样反映每个Set
字段的类型参数:
// you can fill that lookup map upon Producer creation
Map<Type, Field> lookup = new HashMap<>();
for (Field field : Context.class.getDeclaredFields()) {
// this code relies on fact that Set has only one
// type argument which is not parametrized
// also you need of course do some sanity checks
// (i.e field is a type of Set, etc)
Type setType = field.getGenericType();
Type setGenericArgument =
((ParameterizedType) superClass).getActualTypeArguments()[0];
field.setAccisseble(true);
lookup.put(setGenericArgument, field);
}
之后,您可以使用获取的类型信息来实现saveToSet
方法
public void saveToSet(T t) {
// also do exception processing and lookup
// map checks here
Field field = lookup.get(t.getClass());
((Set<T>) field.get(context)).add(t);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.