8

I am trying to learn Java Generics wildcard by reading the following: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103

There is one example in the material:

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)); 
  } 
}

I was wondering if I can change the method signature as the following:

  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) {

Are there any differences between these two method sinatures?

Examples would be appreciated.

Xin
  • 1,169
  • 1
  • 10
  • 20
  • 2
    Found a great explanation here: http://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java/4343547#4343547 – Dzmitry Paulenka Jan 25 '16 at 05:26
  • @DzmitryPaulenka Thanks for your help, but I am still not sure the differences between `List` and `List extends T>` in this case. I think `List` works just like `List extends>`. – Xin Jan 25 '16 at 07:07

2 Answers2

5

You are correct. In this case the two parameter's Type Arguments are being used to express the relationship that dest must contain objects of a super type of the objects in src. Therefore if you say src contains <? extends T> then it's sufficient to say that dest contains objects of T.

You can also express it the other way round, namely:

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

to the same effect.

EDIT: I suspect the author to reinforce the point about the PECS principle

Community
  • 1
  • 1
matt freake
  • 4,877
  • 4
  • 27
  • 56
  • 1
    Not only for "symmetry", but also for undelining the [PECS principle](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) (I don't like the description of this "PECS" thing, as in its short form, it is simply ambiguous and maybe misleading, but with a detailed description, the key point should become clear) – Marco13 Jan 25 '16 at 11:53
5

As matt freake pointed out in his answer, there is not much practical difference between

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)

The snippet below contains an exampleShowingThatTheyAreBasicallyEquivalent.

The reason of why the authors chose to use ? super T is most likely that they wanted to emphasize the PECS principle: Producer extends - Consumer super.

In this example, the first list is a consumer of objects. It only receives objects from the other list. Therefore, its type should be List<? super T>.

However, the snippet below also contains an exampleShowingOneSubtleDifference.I can hardly think of a case where this is really practically relevant, but just to point it out: When you circumvent the type inference, and pin the type <T> to one particular type, you can still pass in a List<? super T> as the first argument to the first method. In the second one, the type has to match exactly - but this is simply what the method signature says, so maybe it's obvious...

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));
        }
    }
}
Community
  • 1
  • 1
Marco13
  • 53,703
  • 9
  • 80
  • 159