2016-08-17 6 views
6

私のクラスはContextのようにしています。Javaはジェネリック型でコレクションを取得します

public class Context { 
    Set<A> sA; 
    Set<B> sB; 
    Set<C> sC; 
} 

それから私は、次のように、Contextクラスからジェネリック型に基づいて操作を実行しようとジェネリッククラスPerformerを、持っている - ことができませんJavaで。私はsaveToSet(B b)ような何かを言う場合

public class Performer<T> { // T can be type of A,B or C 

    public void saveToSet(T t) { 
     Context.Set<T>.add(t); 
    } 
} 

その後、Contextから設定する権利は、この場合sB (Set<B>)には、呼び出されると、新しいインスタンスは、そのセットに追加されます。

質問... Javaでこれをどうやって行うのですか? :D

+2

を実装するために取得した型情報を使用することができます。 – Sinkingpoint

+0

何が問題なのですか? –

答えて

7

悲しいことに、このような方法はイレージャーのためにJavaでは機能しません。

ランタイムにはすべてContext.Set<java.lang.Object>.add(t);が表示され、Setのフィールドがに縮小されているため、あいまいさを解消できません。

あなたはsaveToSet(A a)のような過負荷を書き込むことによってこの問題を回避でき、saveToSet(B b)など

この1を含む多くの方法では、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の回避策の1つに、豊富な種類があります。それらは基本的には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); 

他の可能な方法は、それがGuiceの(マイナス:低パフォーマンス)を注射し、メソッドの傍受と命名された爪に と、このコード(クラスタイプの取り扱いに行われるために必要な最適化がたくさんありますから、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); 
} 
関連する問題