Yesterday, I read about PECS principle from the book effective java. In this post, I want to record my current understanding about this PECS principle by using the ArrayList source code from JDK 1.8.
PECS stands for Producer extends, Consumer super. This is a general principle when applying wild card of Java Generics.
By saying a producer for a generic method, this is trying to imply that the element is giving its element to compute. For example, the addAll
method in ArrayList.java
:
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the
* specified collection's Iterator. The behavior of this operation is
* undefined if the specified collection is modified while the operation
* is in progress. (This implies that the behavior of this call is
* undefined if the specified collection is this list, and this
* list is nonempty.)
*
* @param c collection containing elements to be added to this list
* @return <tt>true</tt> if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
The addAll
method simply take a collection c
and have all the elements in c
added to the current arrayList. It will first convert the collection c
to a Object[]
a. And copy all the elements in a to the current arrayList. Therefore, we can found that the elements the addAll
method used is produced by c
because c
is converted to a
and a is used to perform the array copying method. Here, c is a producer and we need the element in c to be child to c. We use <? extends E>
.
In opposite, a consumer is to provide a mechanism or function to execute. Here is an example from JDK 1.8 in ArrayList.java
:
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
This method is overriding the Iterable.interface
. The action is a consumer to the method. The action provide an actual implementation of the accept(Object)
. For each element in the current arrayList, they are going to be performed the logic in the action.accept(Object)
. Therefore, the action is a consumer and <? super E>
is being used as the parameter for the forEach()
has to provide an implementation of accept()
.
Above is my current understanding of PECS principle.