簡體   English   中英

確保Java中的通用通配符匹配

[英]Ensuring generic wildcards match in Java

因此,據我了解,以下內容:

HashMap<Class<?>,List<?>> map

將允許您插入任何配對。 我如何執行它,以便您只能插入匹配的對。 例如

map.put(String.class, new Vector<String>());
map.put(Integer.class, new Vector<Integer>());

但是請禁止以下操作:

map.put(ClassA.class, new Vector<ClassB>()); //I want this to refuse to compile

這可能嗎?

更新:感謝到目前為止的輸入。 我了解抽象出地圖插入以在兩個參數之間強制使用通用類型。 這樣可以使地圖保持干凈,但是我如何才能向編譯器保證是這種情況。

例如,這將導致編譯器發出抱怨,並且似乎沒有大量的強制修復(至少我嘗試過)

List<String> list1 = map.get(String.class);

所以我目前正在使用以下內容,但我對此不太滿意

List list2 = map.get(String.class);

注意:我目前不在IDE面前,所以這是從內存中獲取的,但希望總的思路很明確。

您可以定義一個泛型類型(在封閉方法的范圍內),然后將其用作泛型Class和泛型Vector (甚至更好的是泛型Collection )的類型。

public <T> void method(Class<T> clazz, Collection<T> collection) {
    Map<Class<T>,Collection<T>> map = new HashMap<Class<T>, Collection<T>>();
    map.put(clazz, collection); //this will compile
}

您可以使用以下命令調用它:

method(String.class, new ArrayList<String>());
//or
method(String.class, new Vector<String>());

並且您可以注意到該語句不會編譯:

method(Integer.class, new Vector<String>());

我不會直接使用HashMap ,因為它的默認方法無法滿足您的需求。 我將基於HashMap編寫自己的集合,該集合將公開它自己的put方法。 它可能看起來像這樣:

public <T> void put(Class<T> clazz, List<T> list) {
    internalMap.put(clazz, list);
}

假設internalMap是此集合類的私有成員,並且類型為HashMap<Class<?>, List<?>> 注意,該方法使用了通用參數。 您可以這樣稱呼:

myCollection.<Integer>put(Integer.class, new Vector<Integer>());
myCollection.<String>put(String.class, new Vector<String>());

甚至像這樣:

myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<String>());

這些將無法編譯:

myCollection.<String>put(String.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<Integer>());

關於更新 由OP最新的評論對我的問題,而這個問題本身的更新

我將如何使其更通用。 例如,用在類級別定義的通用類型替換List。 諸如public void put之類的東西(Class clazz,T1 t1)...如何確保T1可以處理通用參數?

好了,您仍然可以使用附加的通用參數和類型約束來執行此操作。 您需要為想要的集合使用根基類或接口,因為我們需要適當地強制使用泛型。 假定基本類型是Collection<T>接口( VectorArrayList和許多其他集合實現此接口)。 上面的put方法看起來像這樣(我在internalMap定義中包含一個虛構的類):

public class ClassMap {

    private Map<Class<?>, Collection<?>> internalMap 
        = new HashMap<Class<?>, Collection<?>>();

    public <T, TCollection extends Collection<T>> void put(
            Class<T> clazz, 
            TCollection collection) {
        internalMap.put(clazz, collection);
    }

    public <T> Collection<T> get(Class<T> clazz) {
        // Notice the cast, it is important as it will hide
        // the "compiler grumble" you mention in your post
        return (Collection<T>) internalMap.get(clazz);
    }
}

對於帶有顯式泛型參數的情況,用法會略有更新:

myCollection.<Integer, Vector<Integer>>put(Integer.class, new Vector<Integer>());
myCollection.<String, ArrayList<String>>put(String.class, new ArrayList<String>());

和較短的版本:

myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new ArrayList<String>());

由於通用類型推斷 ,也將起作用。

正如您必須指出的那樣, get方法使用Collection<T>接口。 如果我們以與put方法相同的方式引入TCollection通用參數,則現在必須始終提供通用參數。 因此,如果get方法是這樣定義的:

public <T, TCollection extends Collection<T>> TCollection get(Class<T> clazz) {
    return (TCollection) internalMap.get(clazz);
}

那么你有兩個問題

  • 現在,通話需要一些必要的處理
ArrayList<String> list 
 = myCollection.<String, ArrayList<String>>get(String.class);
  • 沒有編譯時的保證 ,該集合確實是一個ArrayList<String> (並且我認為編譯時類型的安全性對您很重要),因此,如果輸入錯誤,您將收到無效類型轉換的運行時異常。 因此, get方法根本不需要引入TCollection泛型參數,可以保證與Collection<T>接口一起很好地工作(因為所有值最終都將實現它)。 如果該接口不適合您(您需要特定的方法等),則可能必須顯式地對其進行轉換(考慮到風險),或者使用更具體的接口作為該接口的根類型。 TCollection約束。 例如,可以使用List<T>代替Collection<T> List<T>

作為底線,關於TCollection泛型參數的使用,可以將其省略。 將集合添加到地圖后,您將不再知道所使用的確切實現。 因此,以上代碼實際上將與此相同:

public class ClassMap {

    private Map<Class<?>, Collection<?>> internalMap 
        = new HashMap<Class<?>, Collection<?>>();

    public <T> void put(Class<T> clazz, Collection<T> collection) {
        internalMap.put(clazz, collection);
    }

    public <T> Collection<T> get(Class<T> clazz) {
        return (Collection<T>) internalMap.get(clazz);
    }
}

並會像這樣簡單地使用:

myCollection.<Integer>put(Integer.class, new Vector<Integer>());
myCollection.<String>put(String.class, new ArrayList<String>());

或類似的:

myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<String>());

這與kocko的建議答案相同

暫無
暫無

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

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