2012-01-13 12 views
7

問題をキーと値の間に制限を適用する方法:ジェネリック:地図に

public static interface FunctionObject<T> { 
     void process(T object); 
    } 

私はそれが一般的な私はTを使用したいので、必要があります:私はクラスで定義された関数オブジェクトのインタフェースをしましたプロセス実装におけるメソッド。
その後、他の一般的なクラスで、私は私が値としてキーと関数オブジェクトとしてのクラスを持っている地図ました:

Map<Class<T>, FunctionObject<T>> map; 

をしかし、私はまた、マップがOFスーパータイプのサブタイプのクラスや関数オブジェクトを受け入れるようにしたいです基本的な考え方は次のようにマップを使用できるようにすることです

Map<Class<? extends T>, FunctionObject<? super T>> map; //not what I need 

//if T were Number, this should be legal 
    map.put(Class<Integer>, new FunctionObject<Integer>(){...}); 
    map.put(Class<Float>, new FunctionObject<Number>(){...}); 
    map.put(Class<Double>, new FunctionObject<Object>(){...}); 

私がしたいとKEY TYPEので、私はこれをしませんでした強制関数オブジェクトは、クラスキーまたはスーパータイプの型を持って、私が実際に定義したいことはこれです:

Map<Class<E extends T>, FunctionObject<? super E>>> map; 

どのように私は、所望の効果を達成することができますか?タイプセーフな異種容器は唯一の選択肢ですか?参照からのデータの入力を許可するには、マップジェネリックタイプはどのように見えますか?

答えて

4

パラメータ化コンテナ、うまく動作するようです:

public class MyMap<T> 
{ 
    interface FunctionObject<X> {} 

    private Map<Class<? extends T>, FunctionObject<Object>> map = new HashMap<>(); 

    @SuppressWarnings("unchecked") 
    public <E extends T> void put(Class<E> c, FunctionObject<? super E> f) 
    { 
     map.put(c, (FunctionObject<Object>) f); 
    } 

    public <E extends T> FunctionObject<Object> get(Class<E> c) 
    { 
     return map.get(c); 
    } 

    public static void Main(String[] args) 
    { 
     MyMap<Number> map = new MyMap<>(); 

     map.put(Integer.class, new FunctionObject<Integer>() {}); 
     map.put(Float.class, new FunctionObject<Number>() {}); 
     map.put(Double.class, new FunctionObject<Object>() {}); 
    } 
} 

編集の質問に遵守すること。悲しいことに、ダウンキャスティングを避ける方法はありません。

が追加されました。get()が追加されました。

+0

ユースケースに応じて動作します。 –

+0

はい、しかし、getを実装することは、楽しみがXDを開始するときです。 –

+0

'get()'は 'FunctionObject ()'のみを返すことができます:パラメータはTとキーの*スーパークラス*なので、上限を確立する方法はありません。私は答えに加えました。 – Viruzzo

4

カプセル化を使用してこれを行うことができます。これは、エントリ単位でこれをチェックする方法でマップを使用することを前提としています。

次のaddメソッドを使用すると、型を2倍にする必要がなくなります。

public class Main { 
interface FunctionObject<T> { } 

private final Map<Class, FunctionObject> map = new LinkedHashMap<Class, FunctionObject>(); 

public <T> void add(FunctionObject<T> functionObject) { 
    Class<T> tClass = null; 
    for (Type iType : functionObject.getClass().getGenericInterfaces()) { 
     ParameterizedType pt = (ParameterizedType) iType; 
     if (!pt.getRawType().equals(FunctionObject.class)) continue; 
     Type t = pt.getActualTypeArguments()[0]; 
     tClass = (Class<T>) t; 
     break; 
    } 
    map.put(tClass, functionObject); 
} 

public <T> void put(Class<T> tClass, FunctionObject<T> functionObject) { 
    map.put(tClass, functionObject); 
} 

public <T> FunctionObject<T> get(Class<T> tClass) { 
    return map.get(tClass); 
} 

public static void main(String... args) throws IOException { 
    Main m = new Main(); 
    m.add(new FunctionObject<Integer>() { 
    }); 
    FunctionObject<Integer> foi = m.get(Integer.class); 
    System.out.println(foi.getClass().getGenericInterfaces()[0]); 
} 
} 

プリント

Main.Main$FunctionObject<java.lang.Integer> 

警告を無効にしたい場合は、@SuppressWarnings("unchecked")を使用することができます。

ポイントは次のとおりです。フィールド宣言にある制約を記述する方法はありませんが、エントリ単位でチェックを行うアクセサメソッドを使用する場合と同じ結果が得られます。生の型が正しいことを確認する必要がある場合は、ランタイムチェックも追加できます。

+0

しかし、これはタイプTしか許されません。ところで、そのMap宣言は、生の型の使用に関する警告を出します。 –

+5

はい、それはピーターの答えのポイントです。型の安全を保証するマップではありません。マップに/から値を入力/取得する唯一の方法は、Funtion とクラスを常に関連付けるこれらのメソッドを使用することです。もちろん、これらのメソッドを呼び出すときに生の型を使用する場合、保証はありません。 –

+0

これで、型保証されたコンテナをコーディングするしかありません。私はこれがC#で同じであるのだろうかと思います。 –