Java Generics — Covariance and Contravariance

Luiz Gustavo De O. Costa
3 min readAug 10, 2021

Since Java 1.5 when the Generics was introduced, the Java developer life changed a lot, and changed in a good way.
In this article will handle Covariance(? extends T) and Contravariance(? super T) and will be splitted into definition, example, and conclusion.

1. Definition

1.1. Covariance

Using the covariance is allowed to use any subtype of FirstClass, such as Suite and Superior instances.

List<? extends T>
// or in the example
List<? extends FirstClass>

1.2. Contravariance

Use elements defined to use in the declaration of its supertypes.

List<? super T>
// or in the example
List<? super Gourmet>

All super classes based on Gourmet are supported. Because of this, a list of Degustacion is not supported.

List<Gourmet> gourmets = Collections.singletonList(Gourmet.INSTANCE); //OK
List<Degustacion> degustacions = Collections.singletonList(Degustacion.INSTANCE); // NOT SUPPORTED
void contravarianceForTheSecondType(List<? super U> items)...
Meal diagram

2. Example

To explain this Java feature, the use case will be based on flights. The flight has sections to seat (First and Second classes), and food to be served (Gourmet, Degustación, Fast..etc).

The point to achieve here is to give an idea about covariance and contravariance, not model a perfect scenario about flights.

Check below the class diagram.

Flight diagram

3. Classes

All classes are available on my GitHub.

4. Stack

  • OpenJDK 11
  • Maven 3

5. Conclusion

Either to guarantee some business logic or to enforce the correct type, it’s clear the benefits of Generics and should be used instead of manually controls even though sometimes is really required.

6. References

--

--

Luiz Gustavo De O. Costa

Hey friend!! I’m Luiz Gustavo, a Java developer and I’m here to learn and write about Java, tests and good practices