在Java中混合嵌套类型参数和通配符

时间:2012-10-24 19:34:26

标签: java generics

为什么要尝试编译

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内时才允许使用通配符。

3 个答案:

答案 0 :(得分:13)

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

但是你传入的Map在每个条目中都有{em>相同类型的List,所以类型不是以相同的方式绑定,也不是相同的自由度。

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

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

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

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

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


我发现键入的版本更容易阅读(和代码),如果您稍后决定您的方法需要知道类型,那么该类型是可供使用的。

答案 1 :(得分:4)

更一般

void accept(Map<String, ? extends List<?>> multiMap)

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