`copy(List<? super T> dest, List<? extends T> src)` 和 `copy(List<T> dest, List<? extends T> src)` 的区别。

8
我正在通过阅读以下内容学习Java泛型通配符:http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103 这个材料中有一个例子:
public class Collections { 
  public static <T> void copy (List<? super T> dest, List<? extends T> src) {
      for (int i=0; i<src.size(); i++) 
        dest.set(i,src.get(i)); 
  } 
}

我想知道是否可以将方法签名更改为以下内容:
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {

  public static <T> void copy(List<T> dest, List<? extends T> src) {

这两个方法签名之间有什么区别吗?如果有示例会更好。

2
在这里找到了一个很好的解释:https://dev59.com/52855IYBdhLWcg3wbzxl#4343547 - Dzmitry Paulenka
@DzmitryPaulenka 感谢您的帮助,但我仍然不确定在这种情况下 List<T>List<? extends T> 之间的区别。我认为 List<T> 的工作方式就像 List<? extends> - Xin
2个回答

5

您是正确的。在这种情况下,两个参数的类型参数被用于表达dest必须包含超类型对象的关系,这些对象在src中。因此,如果您说src包含<? extends T>,那么仅需要说dest包含T对象。

您也可以反过来表达:

List<? super T> dest, List<T> src

达到同样的效果。

编辑:我怀疑作者是为了强调PECS原则


1
这不仅是为了“对称性”,还为了强调PECS原则(我不喜欢这个“PECS”东西的描述,因为在其简短形式中,它只是模棱两可甚至可能会误导人,但通过详细的描述,关键点应该变得清晰明了)。 - Marco13

5

正如matt freake在他的回答中指出的那样,实际上这两者之间没有太大实际区别。

public static <T> void copyA(List<? super T> dest, List<? extends T> src) // and
public static <T> void copyB(List<        T> dest, List<? extends T> src)

下面的代码段包含一个exampleShowingThatTheyAreBasicallyEquivalent
作者选择使用? super T的原因很可能是他们想强调PECS原则:生产者extends - 消费者super
在这个例子中,第一个列表是对象的消费者。 它只从其他列表接收对象。 因此,它的类型应该是List<? super T>
然而,下面的代码段还包含一个exampleShowingOneSubtleDifference。我几乎想不到有什么情况真正实用,但是只是为了指出:当您规避类型推断并将类型<T>固定为一种特定类型时,仍然可以将List<? super T>作为第一个参数传递给第一个方法。 在第二个方法中,类型必须完全匹配-但这只是方法签名所说的,所以可能很明显...
import java.util.List;

public class PecsExample
{
    public static void exampleShowingOneSubtleDifference()
    {
        List<? super Number> superNumbers = null;
        List<Number> numbers = null;

        PecsExample.<Number>copyA(superNumbers, numbers); // Works
        //PecsExample.<Number>copyB(superNumbers, numbers); // Does not work
    }

    public static void exampleShowingThatTheyAreBasicallyEquivalent()
    {
        List<? super Object> superObjects = null;
        List<? super Number> superNumbers = null;
        List<? super Integer> superIntegers = null;

        List<Object> objects = null;
        List<Number> numbers = null;
        List<Integer> integers = null;

        List<? extends Object> extendsObjects = null;
        List<? extends Number> extendsNumbers = null;
        List<? extends Integer> extendsIntegers = null;

        copyA(objects, objects);
        copyA(objects, numbers);
        copyA(objects, integers);
        copyA(numbers, numbers);
        copyA(numbers, integers);
        copyA(integers, integers);

        copyA(superObjects, objects);
        copyA(superObjects, numbers);
        copyA(superObjects, integers);
        copyA(superNumbers, numbers);
        copyA(superNumbers, integers);
        copyA(superIntegers, integers);

        copyA(objects, extendsObjects);
        copyA(objects, extendsNumbers);
        copyA(objects, extendsIntegers);
        copyA(numbers, extendsNumbers);
        copyA(numbers, extendsIntegers);
        copyA(integers, extendsIntegers);

        copyB(objects, objects);
        copyB(objects, numbers);
        copyB(objects, integers);
        copyB(numbers, numbers);
        copyB(numbers, integers);
        copyB(integers, integers);

        copyB(superObjects, objects);
        copyB(superObjects, numbers);
        copyB(superObjects, integers);
        copyB(superNumbers, numbers);
        copyB(superNumbers, integers);
        copyB(superIntegers, integers);

        copyB(objects, extendsObjects);
        copyB(objects, extendsNumbers);
        copyB(objects, extendsIntegers);
        copyB(numbers, extendsNumbers);
        copyB(numbers, extendsIntegers);
        copyB(integers, extendsIntegers);
    }

    public static <T> void copyA(List<? super T> dest, List<? extends T> src)
    {
        for (int i = 0; i < src.size(); i++)
        {
            dest.set(i, src.get(i));
        }
    }

    public static <T> void copyB(List<T> dest, List<? extends T> src)
    {
        for (int i = 0; i < src.size(); i++)
        {
            dest.set(i, src.get(i));
        }
    }
}

感谢提供这些示例,它们非常有帮助。 - Xin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接