Producer Extends Consumer Super (PECS) principle
📅 14 Oct 2018 🕑 3 min readThis approach is used in order to make your API more flexible if you use generics. Before explaining it, I should remind what generic wildcards are in java.
The wildcards - are some bounders for types when we use generics. They can be two types: upper or lower bounded generics. A lower bounded generics can be used in case if we want to use all classes that are parents of a particular class T.
<? super T>
A upper bounded generics can be applied when we want to work with only classes that inherit from a particular class T.
<? extends T>
Ok, that’s all what we need to know to keep going. Let’s return to PECS. The PECS approach says:
Producer - extends, consumer - super
I need to give more details here. If we have a parameter that produces some object with a generic type, we should use a lower bounded generics. But, if a parameter consumes a some value with a generic type, we should use a upper bounded generic generics for this.
As you can see, it is quite easy to remember. However, it is still not clear how to apply it. Have a look at an example.
/**
* add numbers from 0 to count to the collection
* @param collection - collection that numbers will be add to
* @param count - number of elements
*/
void addElements(Collection<? super Integer> collection, int count) {
for (int i = 0; i < count; i++) {
collection.add(i);
}
}
The method above is a consumer, because it fills a collection with a generic type with some elements. According to the PECS approach, in this case we should use super. Indeed, we don’t care about what a certain type of elements in a collection. We just want to know exactly that elements with type Integer can be put into it safely, in terms of type-safety. A upper bounded generic let us do it.
Another method below is a consumer.
/**
* find an index of the element
*
* @param array - list of elements
* @param element - element that we need to find index of
* @param <T> - type of elements
* @return index of the element if the collection contains it otherwise -1
*/
<T> int getIndex(List<? extends T> array, T element) {
int index = -1;
int currentIndex = 0;
for (T e : array) {
if (e.equals(element)) {
index = currentIndex;
break;
}
currentIndex++;
}
return index;
}
In opposite to the previous example, we don’t put elements into a collection. The method just takes elements from a list and compares it to a given parameter. The parameter produces elements.
To sum up, in short words,
if we take elements from a parameter, it is a producer, and if we put elements into a parameter, the parameter is a consumer.