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.
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
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));
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With