簡體   English   中英

T型不是通用的; 它不能用參數參數化 <?> 通用函數中的錯誤

[英]The type T is not generic; it cannot be parameterized with arguments <?> error in a generic function

我想創建一個接受任何Map&a String鍵的泛型函數,如果映射中沒有鍵,那么它應該創建一個Value Type的新實例(傳遞)並將其放入map中然后把它返還。

這是我的實施

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

如果我使用普通(非通用)List作為值類型,它可以工作

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參數generic更改為Map<String, T<?>>它會抱怨the type T is not generic; it cannot be parameterized with arguments <?> 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);

在您的情況下,您需要大多數特定類型的String 在您需要特定類型的情況下, List.class返回Class<List<?>> ,因此您需要添加List.class 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調用方法

第一個示例是因為您使用的是未建議的原始類型。

要解決編譯錯誤,您可能不希望將類限制為T,但允許某些超級T

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

但是你將無法保存轉換,你需要將新實例的結果轉換為(T)。 並且你不能使用List作為類,因為它是一個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

編輯

但是OP期望的基本形式Map<String,List<String> myMap怎么樣?

我們應該知道ArrayListList超類型,但ArrayList<String>不是List<String>超類型,但它是可分配的。

所以讓我們嘗試一下:

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

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

對此的解決方案可能是一些假設,如果class是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]的結果。

由於通用類型信息在運行時被擦除,因此無法使用具有通用類型信息的參數調用泛型方法。

請參閱http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ004

如果你總是使用列表的地圖,那么你可以聲明方法取Map<String, List<T>>

這里的問題是你要求一個Class<T>參數,並且沒有辦法實際獲得一個Class<List<String>>對象(直接)。

List.class為您提供了一個Class<List> ,但是List<String>.class甚至沒有編譯(它在語法上不正確)。

解決方法是將類對象自己轉換為通用版本。 你需要先進行upcast,否則Java會抱怨這些類型不可分配:

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

由於泛型是通過擦除實現的,因此轉換並不能從根本上做任何事情 - 但它使編譯器對T推斷感到滿意,並允許像你這樣靈活的通用方法工作。 當您想要傳遞Class<T>標記時,這不是一種不常見的解決方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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