我有以下地圖
Map<Integer, ? extends Collection<Integer>> map
有時有一個值List,有時有Set一個值。現在我想獲得價值,但有一個問題,它無法編譯。
Collection<Integer> value = map.getOrDefault(1, Collections.emptyList())
我知道我可以做這樣的事情:
Collection<Integer> value = map.get(1);
if (value == null) {
value = Collections.emptyList();
}
但我想了解getOrDefault()方法應該如何與通配符泛型一起使用。
uj5u.com熱心網友回復:
這是一個非常有爭議的案例 - 簡而言之,您無法getOrDefalt()使用上限Value。
default V getOrDefault(Object key, V defaultValue)
作為第二個引數,getOrDefalt()需要一個 type 的實體V。這意味著引數應該與宣告它的Value的型別完全匹配,在這種情況下它是? extends Collection<Integer>.
我們不能使用通配符執行型別轉換。此代碼在語法上無效:
Collection<Integer> value =
m.getOrDefault(1, (? extends Collection<Integer>) Collections.emptyList()); // will not compile
無法提供所需型別的默認值。
uj5u.com熱心網友回復:
簡而言之:您根本不能使用' V' 被宣告為or的任何地圖。除了克隆 JDK 源代碼或使用您自己的方法創建實用方法之外,沒有任何解決辦法。getOrDefault?? extends AnythinggetOrDefault
可以想象一個getOrDefault不會有這個問題的實作。不幸的是,OpenJDK 源代碼根本沒有正確撰寫它,或者我沒有看到一些權衡。不幸的是,“修復它”是向后不兼容的,所以它可能永遠不會發生;我們被損壞的 getOrDefault 困住了。感謝您提出這個問題 - 通過思考它,它讓我意識到它已經壞了。我現在因為在被問到反饋時沒有對 amber-dev 給出反饋而感到難過 :)
原因的解釋有點詳細,需要對泛型如何作業有良好的直覺和知識。戴上你的思考帽,如果你感興趣,請繼續閱讀!
所以為什么?
Map<Integer, ? extends Collection<Integer>> map
好的:這意味著這個map變數可以指向,例如,a new HashMap<Integer, List<Integer>>()。或者,它可以指向 anew HashMap<Integer, Set<Integer>>()甚至 a new HashMap<Integer, Collection<Integer>>()。
map.getOrDefault(....)
呃。這是不可能的。您需要提供一個默認值,并且無論地圖實際指向什么,此默認值都必須“有效” - 它必須與此地圖中的值具有相同的型別。想象一下你有一個Map<Integer, Set<Integer>>- 什么會起作用?嗯,new HashSet<Integer>()會作業。但是什么對所有 3 個(Set<Integer>、List<Integer>和Collection<Integer>,當然還有其他任何集合型別都有效——它們的數量是無限的)?
答案是沒有。
你不能這樣做。你根本不能getOrDefault在這里使用。好吧,除非您null從字面上簡單地通過,這是唯一“所有型別”的值,但是您當然應該只寫.get(k)而不是寫.getOrDefault(k, null)。
固定版本
這是getOrDefault直接從 OpenJDK 源粘貼的 的實作:
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
從根本上說,這永遠不會為您作業-如果V是,您就可以沒有V-? anything那?意思是:我們不知道型別是什么,也沒有代表它的型別變數,所以沒有運算式適合。getOrDefault 的這種做法在這里可以正常作業:
default <Z super V> getOrDefault(Object key, Z defaultValue) {
Z z;
return (((z = get(key) != null) || containsKey(key))
? z
: defaultValue;
}
這里發生的情況是,在這種情況下,雖然地圖可能是一個 Set 或 List 等 - 你只是想要它Collection<Integer>,你實際上并不“關心”,如果默認值不是,你很好事實上,一個'V'(如果你的地圖實際上是 a Map<Integer, List<Integer>>,你不介意呼叫 getOrDefault 最終給你一個不是List<Integer>-的物件Collections.empty())。
因此,我們需要確定有一些新的型別 Z,它是 V 的超型別(從而保證如果鍵在映射中,你會得到一個 V,它肯定是 Z 的一種,因為 Z 是超型別V 根據宣告),并且有一個默認值也絕對是 Z 型別,從而保證在任一“分支”(找到鍵,但未找到鍵)中,回傳值至少為 Z。
但是, map 不能那樣作業,因此你根本不能使用.getOrDefault,。
我認為getOrDefault此時修改不會向后兼容,所以我認為向 openjdk 核心團隊提交功能請求沒有任何意義:他們只會拒絕它,我們堅持使用 getOrDefault 。但是,如果您必須擁有執行上述操作的靜態實用程式方法,則可以創建它。請注意,您可能希望以不同的方式撰寫它 - 轉換為 raw,在 raw 模式下作業,忽略警告,然后清理。理論上,一些 Map 實作可能有不同的 getOrDefault 實作,盡管我很難想象它會是什么樣子(例如,computeIfAbsent它絕對具有關鍵的自定義實作,例如在 ConcurrentHashMap 中)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/494537.html
上一篇:C#如何獲取泛型型別屬性
