簡體   English   中英

Java按泛型類型獲取集合

[英]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.ObjectSet ,因此它將無法消除歧義。

您可以通過編寫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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM