Java泛型:需要捕获#67的?

时间:2010-07-20 13:12:08

标签: java generics

我有这个功能:

 /**
 * 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在for的最后一行突出显示“key”,并给出了这个错误:

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

(由于Java解包,int不是问题,我也试过使用Integers但它不起作用)。

5 个答案:

答案 0 :(得分:7)

问题在于协方差。从一个地图中拉出一个键时,无法保证该键与其他地图兼容。如果一个地图使用Foo,而另一个地图使用Bar,并且这两个类彼此无关,那么您就无法互换它们。

想象一下,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。相反,考虑它的更好方法是用不同的虚构类型名称替换?的每个实例。

警告:数学类比

它类似于 C 常量,它出现在微积分中的不定积分中。例如,∫cos x d = sin + C 。如果你做一个积分然后编写 C 就可以了,但是如果你做了多个积分,那么你必须记住这些常量是不同的并且是不可互换的。然后你应该写 C 1 C 2 C 3

?也是一样的。第一次出现时,假装它是class A。下次是class B。等等。

答案 1 :(得分:3)

?不是Object或其他内容。这完全不为人知。好吧,我不是很擅长解释那些东西,但似乎有一个非常简单的解决方案:既然两个Map的键需要是相同的类型,为什么不引入泛型类型变量呢?

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

答案 2 :(得分: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));
        }
    }

答案 3 :(得分:2)

对于?读取“某些特定类型”并注意,每次提及?时,这可能是不同的类型。因此,例如,a可以是Map<String, Integer>b可以是Map<BigDecimal, Integer>。所以,实际上,你不应该被允许从a中取出一个密钥并将其放入b并且编译器阻止你。编译器在解释这个问题时没什么帮助!

正如其他答案所暗示的那样,您可以将该方法设为通用。

答案 4 :(得分: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()));
    }
}

(我还删除了一些自动装箱/拆箱)