null :o :o :o
为了叙述方便,我把Set改为List,因为Set没有getter方法
List向前兼容,不带泛型的容器。它不做任何泛型检查。例如你可以写:
[code="java"]
List list = new ArrayList();
list.add("s");
String s = (String) list.get(0); //list.get(0)的返回类型是Object
System.out.println(s);
[/code]
完全可以正确运行。
List<?>的?后如果不加extends或者super修饰的话,相当于List<? extends Object>,它是所有List的父类。但在使用List<?>的实例时,方法参数中的泛型参数将无法适配任何实际变量,而返回值中的泛型参数将变为<? extends T>中的T(单独写<?>则取缺省值Object)。例如
[code="java"]
List<? extends Number> list = new ArrayList(3); //容器List<? extends Number>是容器List的父类
//List list2 = new ArrayList(3); //编译出错,容器List不是容器List的父类。
list.add(3); //编译出错。但去掉这行后后续代码能正确运行。
final Number n = list.get(0);
System.out.println(n);
[/code]
使用泛型通配符?可以把容器元素的继承关系推导到容器本身上,但限制了带泛型参数方法的使用。
纯表面解答:
Set里面可以存任意Object,取出对象时一般需要一次类型转换,才能使用该对象
Set<?>,由于加了泛型,强制你只能存指定的Class对象,取出对象时也不需要类型转换,直接使用。
Set<?>和Set的区别在于,前都指定了放入set集合里的对象类型,后都都可以放
比如Set,这样子你只能放字符串进去,放其他的编译器会报错
List<?>主要用于在API中接受某种特定类型容器进行一些只读操作(某些写操作也可以,只要方法参数中不带List中的泛型参数都可以正确调用,例如clear())。List<? extends T>虽然不能add,但get是没有任何问题的。例如你可以写
[code="java"]
//随手打的,未经测试,希望没有问题
double sum(List<? extends Number> numbers) {
double doubleSum = 0;
for (Number num : numbers) {
doubleSum += num.doubleValue();
}
return doubleSum ;
}
[/code]
上面这个函数可以传入List, List, List, List,List等等。但试图传入List就会出错。