In your example you don't use any generic types. You have Panda
extending Animal
, and it's an example of inheritance and leads to polymorphism which is more or less what you describe. Check the links.
To get contravariance, you need to consider some generic type. I'll use .NET type IComparer`1[T]
as an example. With C# syntax (which I'll use rather than Java), we indicate that IComparer
is contravariant in T
by writing in
in the definition:
public interface IComparer<in T>
{
...
}
Suppose I have a method which returns an IComparer`1[Animal]
(or IComaparer<Animal>
), like:
static IComparer<Animal> CreateAnimalComparer()
{
// code that returns something here
}
Now in C#, it's legal to say:
IComparer<Panda> myPandaComparer = CreateAnimalComparer();
Now, this is because of contravariance. Note that the type IComparer<Animal>
does not derive from (or "extend") the type IComparer<Panda>
. Instead, Panda
derives from Animal
, and this leads to the IComparer<Xxxx>
being assignable to each other (in the opposite order, hence "contravariance" (not "covariance")).
The reason why it's meaningful to declare a Comparer<>
contravariant, is if you have a comparer that can compare two arbitrary animals, and return a signed number indicating which is greater, then that same comparer can also take in two pandas and compare those. For pandas are animals.
So the relation
any
Panda
is anAnimal
(from inheritance) leads to the relation
any
IComparer<Animal>
is anIComparer<Panda>
(by contravariance).
For an example with covariance, the same relation
any
Panda
is anAnimal
leads to
any
IEnumerable<Panda>
is anIEnumerable<Animal>
by covariance (IEnumerable<out T>
).