Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them.
The polymorphism applies only to the 'base' type (type of the collection class) and NOT to the generics type.
Generics in Java are implemented using a type erasure mechanism. The compiler translates all type parameters in the source code to their bounding type in the class file. A type parameter's bounding type is Object if a bound type is not specified.
In Java, under the covers, they are the same thing - a List<Object> . This means that if you have one of each, you can't look at them at run time and see what they were declared as - only what they are now. The reified generics would change that, giving Java developers the same capabilities that exist now in .
Here's a relevant excerpt from Java Generics and Collections:
It may be good practice to insert wildcards whenever possible, but how do you decide
which wildcard to use? Where should you use extends
, where should you use super
,
and where is it inappropriate to use a wildcard at all?
Fortunately, a simple principle determines which is appropriate.
The Get and Put Principle: use an
extends
wildcard when you only get values out of a structure, use asuper
wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.
We already saw this principle at work in the signature of the copy method:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
The method gets values out of the source src, so it is declared with an extends
wildcard,
and it puts values into the destination dst, so it is declared with a super
wildcard.
Whenever you use an iterator, you get values out of a structure, so use an extends
wildcard. Here is a method that takes a collection of numbers, converts each to a double,
and sums them up:
public static double sum(Collection<? extends Number> nums) {
double s = 0.0;
for (Number num : nums) s += num.doubleValue();
return s;
}
Well, your second example would allow you to write:
Shape shape = getShapeFromSomewhere();
shapes.add(shape);
whereas you couldn't do that with the first form. It's not useful as often as covariance, I'll grant you.
One area where it can be useful is in terms of comparisons. For example, consider:
class AreaComparer implements Comparator<Shape>
...
You can use that to compare any two shapes... so it would be nice if we could also use it to sort a List<Circle>
for example. Fortunately, we can do that with contravariance, which is why there's an overload for Collections.sort
of:
public static <T> void sort(List<T> list, Comparator<? super T> c)
For example, when implementing the Collections.addAll() method, you need a collection that can contain some type T or a supertype of T. The method then looks like:
public static <T> void addAll(Collection<? super T> collection, T... objects) {
// Do something
}
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