Overview of PECS principle.
Reference: Effective Java: Joshua Bloch
Reference: https://howtodoinjava.com/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super/
Please refer my other github project: https://github.com/mtumilowicz/java11-covariance-contravariance-invariance
-
Covariance:
? extends IntegerList<Integer> ints = new LinkedList<>(); List<? extends Number> nums = ints; -
Contravariance:
? super IntegerList<Integer> ints = new LinkedList<>(); List<? super Integer> nums = ints; -
Invariance: neither covariant nor contravariant
Generics are invarant
-
Remark:
- covariance is read-only
- contravariance is write-only
- otherwise compile-time error
PECS stands for producer-extends, consumer-super:
- Use the
<? extends T>wildcard if you need to retrieve object of typeTfrom a collection. - Use the
<? super T>wildcard if you need to put objects of typeTin a collection.
Properly used, wildcard types are nearly invisible to the users of a class (if the user of a class has to think about wildcard types, there is probably something wrong with its API.)
We provide easy examples of PECS principle (and
we show also examples of bad design - contradiction
of PECS) in PECSTest:
- producer
- constructor of a
LinkedListis a good example of producerpublic LinkedList(Collection<? extends E> c) { this(); addAll(c); } - if we use side function with bad design
then
private static <T> LinkedList<T> fromList_badDesign(Collection<T> list) { // it should be Collection<? extends T> return new LinkedList<>(list); }LinkedList<Animal> dogs = fromList_badDesign(new LinkedList<Dog>()); // compile time error - if we use side function with good design
then
private static <T> LinkedList<T> fromList_goodDesign(Collection<? extends T> list) { return new LinkedList<>(list); }LinkedList<Animal> dogs = fromList_badDesign(new LinkedList<Dog>()); // OK!
- constructor of a
- consumer
addAllmethod ofCollectionsis a good example of consumerboolean addAll(Collection<? super T> c, T... elements) { ... }- suppose we have collection of animals, we want to add
a dog to it, but we don't want to add a dog to the
cats of course.
then
private static void addDog_goodDesign(Collection<? super Dog> c) { c.add(new Dog()); } private static void addDog_badDesign(Collection<Dog> c) { c.add(new Dog()); } private static void addDog_badDesign2(Collection<Animal> c) { c.add(new Dog()); }note that collections are invariant so:addDog_goodDesign(new LinkedList<Animal>()); // addDog_goodDesign(new LinkedList<Cat>()); // compile time error // addDog_badDesign(new LinkedList<Animal>()); // compile time error // addDog_badDesign2(new LinkedList<Dog>()); // compile time erroraddDog_badDesign2(new LinkedList<Dog>()); // compile time error