Covariance
If the generic type's subtype relationship is the same as the type parameter T, then the generic type is covariant w.r.t type parameter T.
A is a subtype of B => Generic<A> is a subtype of Generic<B>
In order to define a generic type as covariant, the extends keyword is used which defines the upper bound.
Example:
public class Animal {}
public class Mammal extends Animal {}
public class Cat extends Mammal {}
public class Dog extends Mammal {}
List<Animal> animals = ...
List<Mammal> mammals = ...
List<Cat> cats = ...
List<Dog> dogs = ...
public void someFunc(List<? extends Mammal> mammals)
|
V
Here 'extends' defines the upper-bound as Mammal
Covariants are producers
New items cannot be added to covariants after their construction.
public void someFunc(List<? extends Mammal> mammals) {
mammals.add(new Cat()); ==> Does not compile
mammals.add(new Mammal()); ==> Does not compile
}
However, one can access the items from covariant types, like so...
public void someFunc(List<? extends Mammal> mammals) {
Mammal m = mammals.get(0); ==> Compiles
}
Also, someFunc() can only accept List<Mammal> or it's subtypes.
someFunc(cats); ==> Compiles
someFunc(mammals); ==> Compiles
someFunc(animals); => Does NOT compile
Contravariance
If the generic type's subtype relationship is the opposite of type parameter T, then the generic type is contravariant w.r.t type parameter T.
A is a subtype of B => Generic<B> is a subtype of Generic<A>
In order to define a generic type as contravariant, the super keyword is used which defines the lower bound.
Example:
public void someFunc(List<? super Mammal> animals)
|
V
Here 'super' defines the lower-bound as Mammal
Contravariants are consumers
New items can be added to contravariants after their construction.
public void someFunc(List<? super Mammal> animals) {
animals.add(new Cat()); ==> Compiles
==> Adding a Cat compiles as the
lower bound does NOT apply to the
constituent type <?> but only to the generic type List<?>
animals.add(new Mammal()); ==> Compiles
}
However, one cannot access the items from contravariant types, like so...
public void someFunc(List<? super Mammal> animals) {
Animal a = animals.get(0); ==> Does not compile
}
Also, someFunc() can only accept List<Mammal> or it's super types.
someFunc(cats); => Does NOT compile
someFunc(dogs); => Does NOT compile
someFunc(mammals); ==> Compiles
someFunc(animals); ==> Compiles
someFunc(objects); ==> Compiles