6

这是集合复制方法(它的一部分):

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)

对于上面的示例调用,

  • 其中第一个限制性太强,因为它只允许在目标和源具有完全相同的类型时调用。(了解)

  • 仅当类型参数为 Object 时,第二个签名才有效

  • 第三个签名仅在类型参数为 Integer 时有效

  • 最后一个签名适用于所有三个类型参数,即 Object、Number 和 Integer。

请解释第二个,第三个和最后一个签名吗?

其余三个对于使用隐式类型参数的调用是等效的,但对于显式类型参数是不同的。

上面这句话是什么意思?

4

1 回答 1

6

让我们一一考虑您的每个签名。

1. public static <T> void copy(List<T> dst, List<? extends T> src)

如果您在没有显式类型参数的情况下调用此方法,则类型参数将被推断为Object,因为您List<Object>作为第一个参数传递。然后List<? extends Object>可以接受一个Integer.

但是,如果您使用显式类型参数调用Number,尽管您可以将 a 传递List<Integer>List<? extends Number>,但对于List<Object>and则不是这样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的消费者,而是实例的生产者。例如,如果您使用显式类型参数调用,则捕获可转换为,类似地,捕获可转换为。因此,这两个参数都是有效的替换。TsrcTNumberList<Object>List<? super Number>List<Integer>List<? extends Number>

因此,在所有 3 种情况下,如果您没有明确提供类型参数,编译器可以正确推断类型参数。但是您应该在这里使用第四个签名,原因是 -

  • dst是实例的消费者T所以它应该使用下限,并且
  • src是实例的生产者T所以它应该使用上限。

相关帖子:

参考:

于 2013-09-02T15:18:33.997 回答