14

为什么尝试编译

public class GenericsFail {
    public static void main(String[] args) {
        accept(new HashMap<String, List<String>>());
    }

    public static void accept(Map<String, List<?>> multiMap) {}
}

给出错误

GenericsFail.java:7: error: method accept in class GenericsFail cannot be applied to given types;
                accept(new HashMap<String, List<String>>());
                ^
  required: Map<String,List<?>>
  found: HashMap<String,List<String>>
  reason: actual argument HashMap<String,List<String>> cannot be converted to Map<String,List<?>> by method invocation conversion

通配符仅在未嵌套在 . 中时才允许使用List

4

3 回答 3

13

原因是?inList<?>可以是“任何东西”,但每个条目中的“任何东西”都不同。Map也就是说,它会List<String>在一个条目中接受 a,在另一个条目中接受 a List<Integer>

但是您传入的 a在每个条目中都Map具有相同的类型List,因此该类型不会以相同的方式或相同的自由度绑定。

“修复”是将类型锁定为特定类型,但仍然是“任何东西” -通过键入方法,每个条目中的“任何东西*”都是相同的:

public static <T> void accept(Map<String, List<T>> multiMap) // complies

或者如果您的方法确实不需要知道哪种类型,请使用通配符来包装类型:

public static void accept(Map<String, ? extends List<?>> multiMap) // compiles

最后一个版本之所以有效,是因为列表的类型虽然是通配符,但在调用时固定为未知但一致的类型。


我发现类型化版本更易于阅读(和代码),并且如果您稍后决定您的方法需要知道类型,则可以使用该类型。

于 2012-10-24T19:48:42.900 回答
4

更一般

void accept(Map<String, ? extends List<?>> multiMap)
于 2012-10-24T20:32:09.963 回答
2

下面是一个简短的例子,说明编译器为什么不能接受这个参数:

    public static void main(String[] args) {
        //if this would be accepted here ...
        accept(new HashMap<String, List<String>>());
    }

    public static void accept(Map<String, List<?>> multiMap) {
        //this is valid considering the type of multiMap parameter,
        //but would not work with the actual passed parameter.
        multiMap.put("test", new ArrayList<Integer>());
    }
于 2012-10-24T20:09:45.237 回答