Java Generics: Producer Extends Consumer Super

I do quite a lot of Java as part of the work I do at Gojek and one of the most helpful, but at times confusing part of the language is generics. Here I will be mentioning an interesting concept I recently came across — PECS or “Producer Extends, Consumer Super”

Photo by Samuel Sianipar on Unsplash

Generics in Java are invariant by default and are applied at compile-time. Polymorphic type arguments do not imply polymorphism in generic types, i.e., You cannot assign to .

However, with and , you achieve “covariance” and “contra-variance” that aids in designing type-safe generic code aware of inheritance and polymorphism.

This is useful in defining methods that accept collection types.

  • Use when the collection is “producing” items that you utilize. Like, iterating over all items of the collection. You only care if the list items are compatible with (i.e., subclasses of ). This is covariance — narrowing of scope.
  • Use when the collection is “consuming” items. A method that copies current results into a list allows you to store objects of type . This is contra-variance — widening of scope.

Examples

? extends T — covariance

You need to apply a over a instance Here the is a “producer.”

interface Rule {void apply(StateMachine m);}class FireWallRule implements Rule {// ...}class StateMachine {
// ...
public void apply(List<? extends Rule> rules) {
// ...
}
}
class Main {
public static void main(String[] args) {
var m = new StateMachine();
var rules = List.of(
new FireWallRule(),
// ...
);
m.apply(rules);
}
}

This allows you to pass a of any objects which are implementations of

? super T — contra-variance

You need to store some instances of , a sub-type of into a . Here the is a “consumer.”

interface Result {
// ...
}
interface FetchResult extends Result {
// ...
}
class Fetcher {
putResult(List<? super FetchResult> output) {
// ...
}
}
class Main {
public static void main(String[] args) {
var result = new ArrayList<Result>();
var fetcher = new Fetcher();
// ...
fetcher.getResult(result);
}
}

This allows to pass any that can store a .

However, such code wont work:

List<? extends CharSequence> list = new ArrayList<>();// ...list.add("hello world"); // error here, cannot add

However, a normal will allow it.

Computer Whisperer. Open-source contributor. Find me at https://amitosh.in/

Computer Whisperer. Open-source contributor. Find me at https://amitosh.in/