variance

Variance refers to how subtyping between more complex types relates to subtyping between their components. 
For example, how should a list of Cats relate to a list of Animals? Or how should a function that returns Cat relate to a function that returns Animal? Let’s assume for a moment that it was possible to use a List<Cat> as a List<Animal>. Then the following code would be legal, given that God is a subclass of Cat as well:
```
List<Cat> cats = new ArrayList<>();
List<Animal> animals = cats; // WRONG
aminals.add(new Dog());
 
// what's a dog doing here?!
Dog dog = cats.get(0);
```
This short source code example demonstrates why it doesn’t make sense to treat a List<Cat> as a List<Animal>. That’s why generic types in Java and C# don’t allow this kind of assignment by default. This behaviour is called invariance.
There are, however, other cases of generic types where assignments like this actually could make sense. For example, using an Iterable<Cat> as an Iterable<Animal> is a reasonable wish. The opposite direction within the inheritance hierarchy of the type parameter is thinkable as well, e.g. using a Comparable<Animal> as a Comparable<Cat>.
So what’s the difference between these generic types: List<T>, Iterable<T>, Comparable<T>? The difference is the “flow” direction of objects of type T in their interface. See details in covariance, contravariance, and invariance.
Neither Java nor C# allow covariance or contravariance for generic types by default. They’re invariant by default. But there are ways and means in both languages to achieve covariance and contravariance.