2010-07-20 66 views
1

我有這樣的功能:Java泛型:需要捕獲#67的?

/** 
* Helper function that adds the values of b to the elements of a, treating 
* all keys that exist in b but not in a, as existent in a with value 0. NB: 
* It operates IN PLACE. 
* @param a The {@link java.util.Map} which will hold the result 
* @param b The {@link java.util.Map} which will be added to a 
*/ 
private static void sumMaps(Map<?, Integer> a, Map<?,Integer> b) 
{ 
    for (Object key : b.keySet()) { 
     Integer currentCount = a.get(key); 
     a.put(key, currentCount == null ? b.get(key) : currentCount + b.get(key)); 
    } 
} 

不過,NetBeans突出「重點」中的最後一行,並給了我這個錯誤:

method put in class java.util.Map<K,V> cannot be applied to given types 
required: capture #67 of?, java.lang.Integer 
found: java.lang.Object, int 

(int是沒有問題的,因爲Java解包,我也試過使用整數,但它沒有工作)。

+0

謝謝大家的回答,他們都提出了大致相同的事情,所以我隨機抽取了一個。 – FrontierPsycho 2010-07-20 13:51:44

+0

注意'Map.entrySet'會給你一個更有效的迭代方式。 – 2010-07-20 14:47:43

回答

3

指定關鍵的意思是它可以是任何類型的,但是對於這個方法工作的每個Map來說鍵必須是相同的。因此,請改爲使用以下代碼:

private static <K> void sumMaps(Map<K, Integer> a, Map<K, Integer> b) { 
     for (K key : b.keySet()) { 
      Integer currentCount = a.get(key); 
      a.put(key, currentCount == null ? b.get(key) : currentCount + b.get(key)); 
     } 
    } 
+0

非常感謝!我曾嘗試過一種泛型類型,但我得到了「未找到類:K」錯誤。我不知道我可以在無效之前「宣佈」這種類型。畢竟這是一個非常簡單的問題。 – FrontierPsycho 2010-07-20 13:51:23

+0

你可以稍微更一般化。第一個地圖鍵可以是第二個地圖鍵的基本類型。 – 2010-07-20 14:47:02

6

問題是協方差之一。當您從一張地圖中拉出一把鑰匙時,您無法保證鑰匙與其他地圖相兼容。如果一個地圖使用Foo s,而另一個地圖使用Bar s並且這兩個類別彼此不相關,則不能交換它們。

想象一下,a的類型爲Map<String, Integer>,而b的類型爲Map<Float, Integer>。這兩種類型與您的參數聲明兼容,但它們的鍵類型不兼容。如果您從b處獲得Float並嘗試將其用作a.put(key,...)中的密鑰,那麼您將遇到麻煩。這實際上是編譯器所抱怨的。使用?不會將映射限制爲兼容的鍵類型。

而不是?您必須指定兩個關鍵類是相同的通過將它們都設置爲一些通用的Key類。這樣一張地圖上的按鍵就可以插入到另一張地圖上的按鍵中。

private static <Key> void sumMaps(Map<Key, Integer> a, Map<Key, Integer> b) 
{ 
    for (Key key : b.keySet()) { 
     Integer currentCount = a.get(key); 
     a.put(key, currentCount == null ? b.get(key) : currentCount + b.get(key)); 
    } 
} 

一般來說,如果你想與?以蘇斯出了問題,做精神與Object取代?。相反,更好的思考方法是用不同的想象類型名稱替換?的每個實例。

警告:數學打個比方

它類似於ç不變的是,在微積分不定積分顯示出來。例如,∫ cos x d x = sin x + C。如果你做一個積分,然後寫C是好的,但如果你做了多個積分,那麼你必須記住,這些常數是不同的,不可互換。然後,它理應你寫ÇÇÇ

?是相同的方式。第一次出現時,假裝它是class A。下一次是class B。等等。

+3

即使它有效 - 我認爲我更喜歡''而不是'','Key'看起來很像類名,但它是一個類型變量。 – 2010-07-20 13:22:35

+0

既然你提起它,我發現有一種令人困惑的習慣是使用像'T','K'和'V'這樣的名字。在我看來,這是一個應該被刪除的C++約定,就像Java放棄了其他形式的縮寫來支持完整拼寫的單詞一樣。 – 2010-07-20 13:32:08

+0

約翰 - 這可能是因爲泛型參數和「真實」類名之間的用法沒有區別。由於泛型參數的使用總是在詞彙上是本地的,所以給它們單字母名稱通常很好,並且這很好地作爲區分它們與實際類別的命名約定。當我查看你的(正確的)代碼片段時,我的大腦自然而然地將'Key'解釋爲一個實際的類,而它將把'K'解釋爲一個通用參數。 – 2010-07-20 13:46:11

2

對於?請閱讀「某些特定的類型」,並且請注意,每次您提及?時,這可能是不同的類型。因此,例如,a可能是Map<String, Integer>b可能是Map<BigDecimal, Integer>。所以,實際上,你不應該被允許從a中取出一個密鑰,並將其寫入b,並且編譯器正在阻止你。編譯器對解釋這個不是很有幫助!

正如其他答案已經建議,你可以使該方法通用。

3

?不是Object什麼的。這完全是未知的。好吧,我並不擅長解釋這些東西,但似乎有一個非常簡單的解決方案:既然Map s的密鑰需要是同一類型,爲什麼不引入泛型類型變量?

private static <T> void sumMaps(Map<T, Integer> a, Map<T, Integer> b) { 
    for (T key : b.keySet()) { 
     Integer currentCount = a.get(key); 
     a.put(key, currentCount == null ? b.get(key) : currentCount 
       + b.get(key)); 
    } 
} 
1

對象不是有效的替代品? 這裏是一個工作版本帶有類型變量T:「?」

private static <T> void sumMaps(final Map<T, Integer> a, 
    final Map<T, Integer> b){ 
    for(final T key : b.keySet()){ 
     final Integer currentCount = a.get(key); 
     a.put(key, 
      currentCount == null 
       ? b.get(key) 
       : Integer.valueOf(currentCount.intValue() 
        + b.get(key).intValue())); 
    } 
} 

(我也刪除了一些自動裝箱/拆箱)

+0

你爲什麼要刪除自動裝箱?我故意使用它。 – FrontierPsycho 2010-07-21 08:03:02

+0

因爲我屬於那些認爲自動裝箱是一個可怕的功能,導致許多預期的錯誤(我總是設置我的日食警告級別警告或自動裝箱錯誤)的人羣。 – 2010-07-21 09:20:34