[英]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.