2016-08-17 60 views
6

比方說,我有我的班級Context這樣。Java通過通用類型獲取集合

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

+2

這裏沒有問題。 – Sinkingpoint

+0

有什麼問題? –

回答

7

不幸的是,這種方法在Java中不起作用,因爲類型擦除

基本上所有的運行時將看到的是Context.Set<java.lang.Object>.add(t);和你的領域,以Set S的java.lang.Object S上的崩潰,所以這將是無法消除歧義。

您可以解決此通過編寫過載像saveToSet(A a)saveToSet(B b)

在許多方面,包括這一個,Java泛型是C++模板的難兄難弟。

0
//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; 
} 
0

這不會工作,由於在Java中稱爲類型擦除的概念。基本上,泛型類型在運行時被擦除,而運行時不會知道它是或者是哪種類型的對象。所以不,你不能做那樣的事情。但是,您可以製作單獨的方法,也可以製作繼承的類並在該重寫方法中實現特定的邏輯。

0

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; 
    } 
} 
+1

這對這個問題有什麼幫助? –

0

也許你可以使用這種方法。

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

其他可能的方式來釘它被命名爲注射方法攔截與吉斯(減:低性能) ,有很多工作需要做這個代碼(類類型處理優化,可從MapMultibinding Guice注入datasets

0

其實你可以使用反射來實現你的目標和繞過類型的擦除。但只有當您的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); 
}