🔙

# a word on PECS

This 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++) {
}
}


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.