9

マップと文字列キーを受け取るジェネリック関数を作成したいのですが、キーがマップに存在しない場合は、値型(渡される)の新しいインスタンスを作成してマップに配置する必要があります。それを返します。

これが私の実装です

public <T> T getValueFromMap(Map<String, T> map, String key, Class<T> valueClass){
    T value = map.get(key);
    if (value == null){
        try {
            value = valueClass.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        map.put(key, value);
    }
    return value;
}

通常の(ジェネリックではない)リストを値型として使用すると機能します。

Map<String,List> myMap;
List value = getValueFromMap(myMap, "aKey", List.class) //works

ただし、ジェネリック型リストではありません

Map<String,List<String>> myMap;
List<String> value = getValueFromMap(myMap, "aKey", List.class) //does not work

Map<String, T>また、マップパラメータを変更してジェネリックにしようとすると、次のMap<String, T<?>>ように文句を言います。the type T is not generic; it cannot be parameterized with arguments <?>

ジェネリックパラメータ自体をジェネリックにすることはできますか?

上記の要件で関数を作成する方法はありますか?

〜更新

皆、洞察に満ちた答えをありがとう。

受け入れられたソリューションがこのコードのすべての値型で機能することを確認しました

    Map<String, Map<String, List<String>>> outer = 
            new HashMap<String, Map<String,List<String>>>();

    Map<String, List<String>> inner = getValueFromMap(outer, "b", 
            (Class<Map<String, List<String>>>)(Class<?>)HashMap.class);

    List<String> list = getValueFromMap(inner, "a", 
            (Class<List<String>>)(Class<?>)ArrayList.class);
4

4 に答える 4

5

あなたの場合にある最も特定のタイプが必要ですString。特定のタイプが必要な場合は、キャストを追加する必要がありますList.classClass<List<?>>Class<List<String>>

 Map<String,List<String>> myMap = new HashMap<String, List<String>>();;
 List<String> value = getValueFromMap(myMap, "aKey",
           (Class<List<String>>)(Class<?>)ArrayList.class) ;

ここで、以下のようなメソッドを呼び出すと

 getValueFromMap(myMap, "aKey",      List.class) 
                ^^^T is List<String>  ^^^ T is List<?> wild char since List.class
                                          will return you Class<List<?>>

したがって、の定義によってTコンパイラが混乱し、コンパイルエラーが発生します。

のインスタンスを作成することはできません。List.classこれはインターフェイスであるため、その実装クラスが必要になるため、でメソッドを呼び出します。ArrayList.class

于 2012-10-25T10:00:56.713 に答える
1

ジェネリック型の情報は実行時に消去されるため、ジェネリック型のパラメーターを使用してジェネリックメソッドを呼び出すことはできません。

http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ004を参照してください

あなたがいつもリストの地図を使うなら、あなたは取る方法を宣言することができるかもしれませんMap<String, List<T>>

于 2012-10-25T10:12:30.170 に答える
1

最初の例は、推奨されていないrawタイプを使用している場合に機能します。

コンパイルエラーを解決するには、クラスをTに制限せずに、Tのスーパーを許可することをお勧めします。

public <T> T getValueFromMap(Map<String, T> map, String key, Class<? super T> valueClass)

ただし、変換を保存する必要はなく、新しいインスタンスの結果を(T)にキャストする必要があります。また、リストはiterfaceであり、インスタンスを作成できないため、クラスとして使用することはできません。

public <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

    if(map.containsKey(key) == false) {
        try {
            map.put(key, (V) valueClass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    return map.get(key);
}

Map<String,ArrayList<String>> myMap = new HashMap<String, ArrayList<String>>();
List<String> value = getValueFromMap(myMap, "aKey", ArrayList.class); //will work

編集

Map<String,List<String> myMapしかし、OPが期待する基本フォームはどうですか?

知っておくべきことArrayListは、のスーパータイプですがList、のスーパータイプでArrayList<String>はありませんList<String>が、割り当て可能です。

それでは、試してみましょう。

例1:getValueFromMap(myMap, "aKey", ArrayList.class); //Compilation error

例2:getValueFromMap(myMap, "aKey", List.class); //Runtime error

これに対する解決策は、クラスがListの場合、ArrayListを作成するという仮定です。

これにより、適切ではないコードを作成する必要があります。

public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

このコードには、ジェネリックスと共通するものは何もありませんが、機能しています。

最後に、

public static void main(String[] args) {

        Map<String,List<String>> myMap = new HashMap<String, List<String>>();
        List<String> value = getValueFromMap(myMap, "aKey", List.class); //does not work

        value.add("Success");

        System.out.println(value);
    }


    public static <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

        if(map.containsKey(key) == false) {
            try {
                map.put(key, (V) createInstace(valueClass));
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return map.get(key);
    }

    public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

そして、これがの場合の結果[Sucess]

于 2012-10-25T10:11:41.700 に答える
1

ここでの問題は、Class<T>パラメータを要求していることであり、実際にClass<List<String>>オブジェクトを(直接)取得する方法はありません。

List.classを提供しますがClass<List>List<String>.classコンパイルすらしません(構文的に正しくありません)。

回避策は、クラスオブジェクトを自分で汎用バージョンにキャストすることです。最初にアップキャストする必要があります。そうしないと、Javaはタイプを割り当てできないと文句を言います。

Class<List<String>> clz = (Class<List<String>>)(Class<?>)List.class;

ジェネリックスは消去によって実装されるため、キャストは基本的に何もしませんが、コンパイラーが何であるかを推測Tできるようにし、あなたのような柔軟なジェネリックスメソッドを機能させます。Class<T>これは、トークンを渡すときに使用する必要がある珍しい回避策ではありません。

于 2012-10-25T10:21:37.977 に答える