What Is PECS in Java? Producer Extends, Consumer Super - Quiz
Total: 7 questions
1. What is the PECS principle in Java?
What is the PECS principle in Java?
PECS (Producer Extends, Consumer Super) is the rule for choosing the bound of a wildcard in a generic method parameter. The choice depends on the role of the parameter in the method:
- the parameter is a producer (a source), the method only reads
Tvalues from it →<? extends T>; - the parameter is a consumer (a destination), the method only writes
Tvalues into it →<? super T>; - the method both reads and writes → the exact type
Twith no wildcard.
Short form: take out — extends, put in — super. The name was coined by Joshua Bloch in “Effective Java” (3rd edition, Item 31). Example: sum(List<Number>) does not accept a List<Integer> because generic types are invariant, while sum(List<? extends Number>) does — the list is only read.
2. Which lists can be passed to a List<? extends Employee> parameter and to a List<? super Employee> parameter, and what can the method do with them?
Which lists can be passed to a List<? extends Employee> parameter and to a List<? super Employee> parameter, and what can the method do with them?
For the hierarchy Person ← Employee ← Manager:
List<? extends Employee>(upper bounded, producer) acceptsList<Employee>andList<Manager>.get()returnsEmployee, and the only value you can add isnull:add(new Employee())fails withincompatible types: Employee cannot be converted to CAP#1, because the argument may be aList<Manager>.List<? super Employee>(lower bounded, consumer) acceptsList<Employee>,List<Person>,List<Object>. You can addEmployee,Managerandnull, butget()returns onlyObject— aninstanceofcheck is needed after that.List<?>accepts any list, reads elements asObjectand allows adding onlynull. That is the difference fromList<Object>, which accepts any element but cannot receive aList<String>.
3. Why can you pass a List<Person> to a List<? super Employee> parameter but cannot call add(new Person()) on it?
Why can you pass a List<Person> to a List<? super Employee> parameter but cannot call add(new Person()) on it?
This is not an exception to PECS but two consequences of the same bound. The compiler allows only operations that are safe for any list that may stand behind ? super Employee: List<Employee>, List<Person> or List<Object>.
add(new Employee())andadd(new Manager())are safe: such an object is anEmployee, aPersonand anObjectat the same time.add(new Person())would fit aList<Person>, but if aList<Employee>was passed, it would receive an object that is not anEmployee. The compiler does not know which list was passed, so it rejects the call.
The same bound applies when passing an argument, when assigning (List<? super Employee> list = persons; compiles, = managers; does not) and when adding elements. So the claim “PECS works only when assigning collections and does not apply to adding elements” is wrong.
4. How do you apply PECS in your own method that moves elements from one list to another?
How do you apply PECS in your own method that moves elements from one list to another?
PECS is applied to each parameter separately: the source gets extends, the destination gets super.
// candidates is a producer, staff is a consumer
static void hireAll(List<? extends Employee> candidates, List<? super Employee> staff) {
for (Employee e : candidates) {
staff.add(e);
}
}
List<Manager> managers = List.of(new Manager(), new Manager());
List<Person> persons = new ArrayList<>();
hireAll(managers, persons);
System.out.println(persons.size()); // 2
If both parameters were declared as List<Employee>, the call hireAll(managers, persons) would not compile because of either argument. With wildcards the method accepts the widest range of type combinations and stays type-safe.
5. How is the PECS principle reflected in the signatures of the Java standard library?
How is the PECS principle reflected in the signatures of the Java standard library?
JDK methods that read from one parameter and write to another are declared by PECS:
Collections.copy(List<? super T> dest, List<? extends T> src)—srcis the producer,destthe consumer;Collections.addAll(Collection<? super T> c, T... elements)— the collection is a consumer;Collection.addAll(Collection<? extends E> c)— the argument is a producer;List.sort(Comparator<? super E> c)— the comparator consumes the elements;Collections.max(Collection<? extends T> coll, Comparator<? super T> comp)— the collection is a producer, the comparator a consumer.
A Comparator<T> receives objects in compare(), so it consumes them and is declared as ? super T. Thanks to that, a single Comparator<Object> such as Comparator.comparing(Object::toString) also sorts a List<Integer>: [10, 9, 100] becomes [10, 100, 9] (string comparison).
6. Why does Collections.copy() fail with an empty destination list even though the types match by PECS?
Why does Collections.copy() fail with an empty destination list even though the types match by PECS?
PECS is about types, not size. Collections.copy(dest, src) replaces elements of dest by index and never adds new ones: if dest.size() < src.size(), it throws IndexOutOfBoundsException: Source does not fit in dest. If dest is longer, its tail stays: copying [3, 1, 2] into the List<Number> [0, 0, 0, 0.5] gives [3, 1, 2, 0.5].
To copy into an empty list, use dest.addAll(src) or new ArrayList<>(src).
7. What are the most common mistakes with PECS and wildcards?
What are the most common mistakes with PECS and wildcards?
- Expecting
List<Manager>to be a subtype ofList<Employee>. Generic types are invariant; declare a source parameter asList<? extends Employee>. - Adding to a
List<? extends T>— the error iscannot be converted to CAP#1. If the method writes to the collection, use? super Tor the exactT. - Treating
? extendsas protection against changes:remove(),clear()andadd(null)still work. For immutability useList.copyOf()orCollections.unmodifiableList(). - Reading from a
List<? super T>asT— the element type isObject. If you need to both read asTand write, use the exact typeList<T>. - Putting a wildcard in a return type (
List<? extends Number> load()): the caller cannot add anything to the result. Use wildcards only in parameters. - Trying to give a wildcard two bounds:
<? extends A super B>does not compile — a wildcard has exactly one bound.