What is PECS (Producer Extends Consumer Super)?
tl;dr: "PECS" is from the collection's point of view. If you are only pulling items from a generic collection, it is a producer and you should use extends
; if you are only stuffing items in, it is a consumer and you should use super
. If you do both with the same collection, you shouldn't use either extends
or super
.
Suppose you have a method that takes as its parameter a collection of things, but you want it to be more flexible than just accepting a Collection<Thing>
.
Case 1: You want to go through the collection and do things with each item.
Then the list is a producer, so you should use a Collection<? extends Thing>
.
The reasoning is that a Collection<? extends Thing>
could hold any subtype of Thing
, and thus each element will behave as a Thing
when you perform your operation. (You actually cannot add anything (except null) to a Collection<? extends Thing>
, because you cannot know at runtime which specific subtype of Thing
the collection holds.)
Case 2: You want to add things to the collection.
Then the list is a consumer, so you should use a Collection<? super Thing>
.
The reasoning here is that unlike Collection<? extends Thing>
, Collection<? super Thing>
can always hold a Thing
no matter what the actual parameterized type is. Here you don't care what is already in the list as long as it will allow a Thing
to be added; this is what ? super Thing
guarantees.
Java generics PECS
A nice mnemonic you can use is to imagine returns
for extends
and accepts
for super
.
So a Tree<? extends T>
reads Tree<? returns T>
, which means that you can call the methods in Tree
that return T
, but not the methods that accept T
as an argument type.
Why Comparable and Comparator are consumers in PECS wildcard types in Java
A nice analogy can be drawn to the interfaces Consumer<T>
and Supplier<T>
(with Supplier
being analogous to Producer). A Consumer<T>
is a function that takes in a T
, while a Supplier<T>
is a function that returns a T
. Notice that we are talking about method signatures and return type, we say nothing about the semantics of the method. This is a core property of PECS: it is independent of the semantics and can be determined solely on the signature and return type of the methods used.
Looking at Comparable<T>
and Comparator<T>
, we find that both have methods (int compareTo(T)
and int compare(T, T)
) that take in, i.e. consume, T
's.
For the collections, we have to look on how we use the collection, i.e. if we are using producer- or consumer-methods:
- If we retrieve data from the collection (
T get(int)
, iterator, ...), the list produces values for us and we use? extends T
. - If we use the collection to store data, (i.e. we call
add(T)
,addAll(Collection<T>)
,contains(T)
, ...), we call consuming methods, thus the method is a consumer of our data and we use? super T
. - If we use a collection to both store and retrieve values, the collection acts as a consumer and producer at the same time, thus we have to use the precise
T
, neither using... extends ...
nor... super ...
.
Difference between ? super T and ? extends T in Java
extends
The wildcard declaration of List<? extends Number> foo3
means that any of these are legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
Reading - Given the above possible assignments, what type of object are you guaranteed to read from
List foo3
:- You can read a
Number
because any of the lists that could be assigned tofoo3
contain aNumber
or a subclass ofNumber
. - You can't read an
Integer
becausefoo3
could be pointing at aList<Double>
. - You can't read a
Double
becausefoo3
could be pointing at aList<Integer>
.
- You can read a
Writing - Given the above possible assignments, what type of object could you add to
List foo3
that would be legal for all the above possibleArrayList
assignments:- You can't add an
Integer
becausefoo3
could be pointing at aList<Double>
. - You can't add a
Double
becausefoo3
could be pointing at aList<Integer>
. - You can't add a
Number
becausefoo3
could be pointing at aList<Integer>
.
- You can't add an
You can't add any object to List<? extends T>
because you can't guarantee what kind of List
it is really pointing to, so you can't guarantee that the object is allowed in that List
. The only "guarantee" is that you can only read from it and you'll get a T
or subclass of T
.
super
Now consider List <? super T>
.
The wildcard declaration of List<? super Integer> foo3
means that any of these are legal assignments:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from
List foo3
:- You aren't guaranteed an
Integer
becausefoo3
could be pointing at aList<Number>
orList<Object>
. - You aren't guaranteed a
Number
becausefoo3
could be pointing at aList<Object>
. - The only guarantee is that you will get an instance of an
Object
or subclass ofObject
(but you don't know what subclass).
- You aren't guaranteed an
Writing - Given the above possible assignments, what type of object could you add to
List foo3
that would be legal for all the above possibleArrayList
assignments:- You can add an
Integer
because anInteger
is allowed in any of above lists. - You can add an instance of a subclass of
Integer
because an instance of a subclass ofInteger
is allowed in any of the above lists. - You can't add a
Double
becausefoo3
could be pointing at anArrayList<Integer>
. - You can't add a
Number
becausefoo3
could be pointing at anArrayList<Integer>
. - You can't add an
Object
becausefoo3
could be pointing at anArrayList<Integer>
.
- You can add an
PECS
Remember PECS: "Producer Extends, Consumer Super".
"Producer Extends" - If you need a
List
to produceT
values (you want to readT
s from the list), you need to declare it with? extends T
, e.g.List<? extends Integer>
. But you cannot add to this list."Consumer Super" - If you need a
List
to consumeT
values (you want to writeT
s into the list), you need to declare it with? super T
, e.g.List<? super Integer>
. But there are no guarantees what type of object you may read from this list.If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g.
List<Integer>
.
Example
Note this example from the Java Generics FAQ. Note how the source list src
(the producing list) uses extends
, and the destination list dest
(the consuming list) uses super
:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
Also see
How can I add to List<? extends Number> data structures?
Java wildcards and generics ? super T and ? extends T
you should have a look at the explanation of PECS principle
What is PECS (Producer Extends Consumer Super)?
In short, when you want to get information from an object, make sure to use extends with the wild card.
And when you want to put information into an object, make sure to use super along with wild card
Use of '? extends ' and '? super ' in Collection generics
The wildcards introduce restrictions in how the collection can be used.
For example, with List<? extends Number>
, I can't add new elements to the list. This is because all I know is that the list is some kind of subtype of Number
, but I don't know what that actual subtype is (so how could I know what to add?). For example, take the following code:
public void doSomethingWith(List<? extends Number> numbers) {
numbers.add(Integer.valueOf(0)); // Won't compile
}
This won't compile because both of these method calls are legal:
doSomethingWith(new ArrayList<Integer>());
doSomethingWith(new ArrayList<Double>());
What you can do is read elements from the list:
// This will all compile
public void doSomethingWith(List<? extends Number> numbers) {
for (Number number : numbers) {
// Do something with number
}
// OR
Number number = numbers.get(0);
// OR
Number number = numbers.remove(0);
}
Calls to methods like get
will return some kind of Number
, we know that for a fact because of the ? extends Number
, so we can treat it like that for reading purposes.
On the other hand, List<? super Integer>
has exactly the opposite result. I can no longer read from the list, but I can write to it. I know that whatever ?
is, it will definitely be a super-class of Integer
, so concrete types of the list will definitely accept Integer
values. For example:
public void doSomethingWith(List<? super Integer> integers) {
integers.add(Integer.valueOf(0));
}
That code is completely legal. However, if you want to read from the list, the only way to do this is to use Object
since anything else requires casting (which requires knowing its concrete type):
for (Object obj : integers)
// OR
Object obj = integers.get(0);
// OR
Object obj = integers.remove(0);
What's Really Happening
Here's what's actually happening. When you specify ? extends Number
, you're making any method that takes elements as a parameter unusable. In fact, if you try to auto-complete code in Eclipse using Ctrl+Space on a List<? extends Number>
, it shows null
as the parameters' types in the add
methods and the like. Meanwhile, all the methods that return elements are guaranteed to return at least some kind of Number
, though you won't know exactly which subclass of Number
it might actually be.
When you specify ? super Integer
, you're making any method that takes elements as a parameter guarantee that they'll accept Integer
values (and sub-classes of Integer
as well). This allows you to call methods like add
since you know they'll accept Integer
types. Meanwhile, all methods that return elements are only guaranteed to return something, but we don't know what, so all the methods that return elements are only guaranteed to return Object
.
PECS is an excellent acronym to remember this, it means "Producer Extends, Consumer Supers". This means that if you want your list to give you something, it's a producer, and you should use extends
. If you want your list to accept things from you, it's a consumer, so you use super
. See this answer for more.
But what if I have a wildcard with no bounds?
It does both! <?>
restricts you from calling methods that take the generic type as an argument and causes all the methods that return the generic type to return Object
. This is because we have no idea what the type is whatsoever. For example, all of these assignments into a List<?>
are legal:
List<?> list;
list = new ArrayList<Integer>();
list = new ArrayList<String>();
list = new ArrayList<MyClass>();
And so on.
Related Topics
What Are the Pros and Cons of the Leading Java HTML Parsers
How to Get Url from Firebase Storage Getdownloadurl
The Asynctask API Is Deprecated in Android 11. What Are the Alternatives
Why Retrieving Google Directions For Android Using Kml Data Is Not Working Anymore
How to Set Java_Home in Linux For All Users
What Causes a Java.Lang.Arrayindexoutofboundsexception and How to Prevent It
Sort Arraylist of Custom Objects by Property
Avoiding Nullpointerexception in Java
How to Determine Whether an Array Contains a Particular Value in Java
How to Read Xml Using Xpath in Java
Converting HTML to Pdf Using Itext
Android.Content.Res.Resources$Notfoundexception: String Resource Id #0X0
How to Read a Text File in Android
How to Parse Jsonarray in Android
How to Stop Java Process Gracefully
The Use of Multiple Jframes: Good or Bad Practice
Why Does My Arraylist Contain N Copies of the Last Item Added to the List