Java泛型如何接受泛型参数中的任何派生类型

Java泛型如何接受泛型参数中的任何派生类型,java,generics,Java,Generics,在下面的两行代码中 HashMap<Integer, ?extends Collection<String>> map= new HashMap<Integer, TreeSet<String>>(); map.put(1,new TreeSet<String>()); HashMap= 新的HashMap(); put(1,新树集()); 第2

在下面的两行代码中

HashMap<Integer, ?extends Collection<String>> map=
                                           new HashMap<Integer, TreeSet<String>>();

map.put(1,new TreeSet<String>());
HashMap=
新的HashMap();
put(1,新树集());
第2行:HashMap>类型中的方法put(Integer,capture#1-of?extends Collection)不适用于参数(int,TreeSet)

第1行:这没有错误

为什么在第1行中允许相同的泛型类型(TreeSet),但在第2行中不允许

编辑: 使用super而不是extends时,为什么不允许出现以下情况

HashMap<Integer, ?super Collection<String>> map=new HashMap(<Integer, TreeSet<String>>());
HashMap-map=newhashmap(());
但是

HashMap map=newhashmap();
put(1,新树集());

允许

得到编译器错误的原因与不能向
列表添加
狗的原因相同映射声明告诉编译器映射中的值是字符串的某个集合。在运行时,它可以是
TreeSet
,但也可以是其他集合类型。因此,编译器不能允许放置
TreeSet
,因为它实际上可能包含
ArrayList

更一般地说,每当使用
通配符使用绑定类型参数时,实际上只允许从映射中读取(例如,迭代其元素)。换句话说,您可以随时执行以下操作:

for(Iterator<? extends Collection<String>> iterator = map.values().iterator(); iterator.hasNext();) {
    Collection<String> collection = iterator.next();
    ... 
}
编译器说:

找不到适合put的方法(int、TreeSet) put(1,新树集()); 方法HashMap.put(整数,CAP#1)不适用 (无法通过方法调用转换将实际参数树集转换为CAP#1) 方法AbstractMap.put(整数,CAP#1)不适用 (实际参数树集无法通过方法调用转换转换为CAP#1),其中CAP#1是一个新类型变量: 第1章扩展了从捕获?扩展集合

我认为这是因为编译器的类型不明确,如前所述:


映射的声明告诉编译器 地图是一些字符串的集合。在运行时,它可能是一个 树集,但也可能是其他集合类型

如果编译器的类型是确定的,则代码将编译并运行而不会出现任何问题,例如,您可以按以下方式编写代码,从而成功编译并运行:

public <T extends Collection<String>> void someVoid(){
        //...
        //...
        HashMap<Integer, T > map
                = new HashMap<>();
        map.put(1,(T) (new TreeSet<String>()));
        //...
        //...
    }
public void someVoid(){
//...
//...
哈希映射
=新HashMap();
map.put(1,(T)(新树集());
//...
//...
}

在阅读了其他答案并对原始问题进行了更多思考(实际上是关于“
”扩展部分与不扩展部分的比较),我提出了以下示例,应该更容易理解

假设我们有这个方法:

void doSomething(Collection<? extends Number> numbers) { ... }

在这种情况下,该方法可以向集合中添加任何数字,但是像
doSomething(integers)
这样的调用会成为编译器错误,因为
Integer
的集合只能接受整数。因此,编译器再次防止以后出现
ClassCastException
s,确保代码是类型安全的。

或者,如果映射必须接受任何类型的字符串集合,
map map=new HashMap(),可以简化为
Map Map=newhashmap()@JBNizet更好;我将把它添加到我的答案中。如果他将树集转换为集合,他可以将其放入mapA
Map
不是一个
Collection
,因此
新HashMap…无论什么…
都不匹配
?扩展集合
@achabahe No。它与调用
put
时作为值传递的内容的类型无关,与通配符无关。
HashMap<Integer, Collection<String>> map =
         new HashMap<Integer, Collection<String>>();
HashMap<Integer, ? super Collection<String>> map = new HashMap<Integer, TreeSet<String>>());
HashMap<Integer, ? super Collection<String>> map = new HashMap<>();
map.put(1, new TreeSet<String>());
for(Iterator<? extends Collection<String>> iterator = map.values().iterator(); iterator.hasNext();) {
    Collection<String> collection = iterator.next();
    ... 
}
HashMap<Integer, TreeSet<String>> map = new HashMap<>(); // In Java 7+, you can use the diamond operator when creating the HashMap
public <T extends Collection<String>> void someVoid(){
        //...
        //...
        HashMap<Integer, T > map
                = new HashMap<>();
        map.put(1,(T) (new TreeSet<String>()));
        //...
        //...
    }
void doSomething(Collection<? extends Number> numbers) { ... }
Collection<Integer> integers = asList(1, 2, 3);
Collection<Long> longs = asList(1L, 2L, 3L);
doSomething(integers);
doSomething(longs);
void doSomething(Collection<Number> numbers) { ... }