这是Collections复制方法(部分内容):
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
有4个样本电话:
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
1. Collections.copy(objs, ints);
2. Collections.<Object>copy(objs, ints);
3. Collections.<Number>copy(objs, ints);
4. Collections.<Integer>copy(objs, ints);
以上呼叫的工作原理是什么?
我们还可以使用几种可能的签名来声明该方法
1. public static <T> void copy(List<T> dst, List<T> src)
2. public static <T> void copy(List<T> dst, List<? extends T> src)
3. public static <T> void copy(List<? super T> dst, List<T> src)
4. public static <T> void copy(List<? super T> dst, List<? extends T> src)
对于上面的示例调用,
其中第一个限制性太强,因为它只允许调用 目的地和来源具有完全相同的类型。的(被理解)
第二个签名仅在type参数为Object
第三个签名仅在type参数为Integer
最后一个签名适用于所有三种类型参数 - 即。,Object, 数字和整数。
请解释第二,第三和最后一个签名?
其余三个对于使用的呼叫是等效的 隐式类型参数,但显式类型参数不同。
以上陈述的含义是什么?
答案 0 :(得分:5)
让我们逐一考虑你的每个签名。
1. public static <T> void copy(List<T> dst, List<? extends T> src)
如果在没有显式类型参数的情况下调用此方法,则将类型参数推断为Object
,因为您将List<Object>
作为第一个参数传递。然后List<? extends Object>
可以接受Integer
。
但是,如果使用显式类型参数Number
进行调用,虽然您可以将List<Integer>
传递给List<? extends Number>
,但List<Object>
和{{1}的情况也是如此因为泛型是不变的。
List<Number>
对于隐式类型参数,2. public static <T> void copy(List<? super T> dst, List<T> src)
将被推断为T
,因为您将Integer
作为第二个参数传递给List<Integer>
。然后List<T>
是List<Object>
的有效替代。
如果使用显式类型参数List<? super Integer>
调用方法,则可以将Number
作为第一个参数传递,但不能将List<Object>
作为第二个参数传递。出于与上述相同的原因。
List<Integer>
现在,此方法签名适用于任何类型。对于推断的任何类型参数,3. public static <T> void copy(List<? super T> dst, List<? extends T> src)
是dst
实例的使用者,而T
是src
实例的生成者。例如,如果使用显式类型参数T
进行调用,则Number
可以转换为List<Object>
,类似地,List<? super Number>
可以转换为List<Integer>
。因此,两个参数都是有效替换。
因此,在所有3种情况下,如果您没有明确提供类型参数,编译器可以正确推断类型参数。但你应该在这里使用第四个签名 -
List<? extends Number>
是dst
个实例的使用者,所以它应该使用下限,T
是src
实例的 producer ,所以它应该使用上限。相关文章:
<强>参考:强>