2015-11-07 66 views
2

我遇到了java中泛型的问题,尤其是通配符。我有一个MapouterMap),其值是另一个MapinnerMap)元素,其值为List元素。嵌套通配符编译失败调用方法

请参阅下面的java类,我已创建该类来演示此行为。不包括进口报表。忽略使用方法里面流,那是无关紧要的:

public class SSCCE { 
    public static void main(String[] args) { 
    // Setup 
    List<Double> doubles = Arrays.asList(1.0,2.0,3.0); 
    Map<Integer, List<Double>> innerMap = new HashMap<>(); 
    innerMap.put(1, doubles); 
    Map<String, Map<Integer, List<Double>>> outerMap = new HashMap<>(); 
    outerMap.put("hello", innerMap); 

    // Test  
    // This method call works fine. 
    Stream<Integer> streamFromInner = getStreamOfMappingToN(innerMap); 

    // This method call does not work - causes below compilation error. 
    Stream<Integer> stream = getStreamOfMergedDistinctMappingToN(outerMap); 
    } 

    private static <T, U> Stream<T> getStreamOfMappingToN(Map<T, ? extends Collection<U>> map) { 
    return map.entrySet().stream().map(q -> q.getKey()); 
    } 

    private static <T, U> Stream<T> getStreamOfMergedDistinctMappingToN(Map<String, Map<T, ? extends Collection<U>>> map) { 
    return map.entrySet().stream().flatMap(
      p -> getStreamOfMappingToN(p.getValue()) 
    ).distinct(); 
    } 
} 

我看到下面的编译错误,在第二个方法调用:

method getStreamOfMergedDistinctMappingToN in class SSCCE cannot be applied to given types; 
    required: Map<String,Map<T,? extends Collection<U>>> 
    found: Map<String,Map<Integer,List<Double>>> 
    reason: cannot infer type-variable(s) T,U 
    (argument mismatch; Map<String,Map<Integer,List<Double>>> cannot be converted to Map<String,Map<T,? extends Collection<U>>>) 
    where T,U are type-variables: 
    T extends Object declared in method <T,U>getStreamOfMergedDistinctMappingToN(Map<String,Map<T,? extends Collection<U>>>) 
    U extends Object declared in method <T,U>getStreamOfMergedDistinctMappingToN(Map<String,Map<T,? extends Collection<U>>>) 

任何人可以说明为什么发生这种情况与第二方法调用,而不是第一个?两个方法签名均包含Map<T, ? extends Collection<U>>,但只有第二个方法无法与呼叫相匹配。如果我用List<U>替换有问题的? extends Collection<U>,它可以正常工作,因为我实际上将它传递给List,但这看起来很懒惰,并不能解释为什么我看到此错误。

我已经在这方面做了一些阅读,有些人有类似的问题,并解释了他们的问题 - 许多人认为通配符的级别很重要 - 但没有理解这里发生了什么,我很难将其他解决方案与这个问题联系起来。它仍然很混乱。通过阅读,我发现如果我在第二个方法声明中将Map<T, ? extends Collection<U>>替换为? extends Map<T, ? extends Collection<U>>,它编译得很好,但我不明白为什么,以及为什么第一个方法签名可以工作。

我希望你对此反馈意见。

+0

这个代码更实际的问题(即使你编译它),是很难弄清楚发生了什么。 – biziclop

+0

@biziclop当然,这不是我的实际代码,它只是表现了问题。基于'Collection'中的内容,我正在使用流从'Entry'映射到'T',但是我留下了很多内容,因为我不认为它与问题相关。T和U也是一样,它们可以是任何东西。 –

+0

http://bayou.io/draft/Capturing_Wildcards.html#Nested_Wildcards – ZhongYu

回答

1

你应该改变你的方法如下:

private static <T, U extends Collection<?>> Stream<T> getStreamOfMappingToN(Map<T, U> map) { 
    return map.entrySet().stream().map(q -> q.getKey()); 
} 

private static <T, U extends Collection<?>> Stream<T> getStreamOfMergedDistinctMappingToN(Map<String, Map<T, U>> map) { 
    return map.entrySet().stream() 
      .flatMap(p -> getStreamOfMappingToN(p.getValue())).distinct(); 
} 

什么改变的是,代替声明输入类型为? extends Collection<U>,该方法被接受Map<T, U>U约束到U extends Collection<?>

此更改代表您真正想要执行的操作:您输入Map作为输入。该地图有TU两种类型。 T可以是我们给出的任何类型,但U确实需要是某种东西的Collection,即U必须extends Collection<?>


要找出为什么你的例子不能编译,请参考this answer(或this one):通配符只适用于第1级,不得更深。正如你发现的那样,你需要添加? extends Map<...>来对抗通配符不被递归应用的事实。

不过,我会建议你使用第一个解决方案,因为我觉得它提供了更清晰的代码和正确的意图,而不是乱搞通配符。

相关问题