[英]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>
接口( Vector
, ArrayList
和許多其他集合實現此接口)。 上面的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.