Categories
bounded-wildcard generics java pecs super

What is PECS (Producer Extends Consumer Super)?

854

I came across PECS (short for Producer extends and Consumer super) while reading up on generics.

Can someone explain to me how to use PECS to resolve confusion between extends and super?

1

  • 5

    A very good explanation with an example @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 which explains super part but, gives an idea of another.

    Jan 20, 2019 at 6:22

982

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.

14

  • 187

    I’m always trying to think about it this way: A producer is allowed to produce something more specific, hence extends, a consumer is allowed to accept something more general, hence super.

    May 7, 2013 at 13:11

  • 11

    Another way to remember the producer/consumer distinction is to think of a method signature. If you have a method doSomethingWithList(List list), you are consuming the list and so will need covariance / extends (or an invariant List). On the other hand if your method is List doSomethingProvidingList, then you are producing the List and will need contravariance / super (or an invariant List).

    – Raman

    Jan 24, 2014 at 19:20


  • 13

    @Raman, I think you just confused it. In doSthWithList( you can have List<? super Thing> ), since you are a consumer, you can use super (remember, CS). However, it’s List<? extends Thing> getList() since you are allowed to return something more specific when producing (PE).

    May 27, 2014 at 19:08

  • 6

    @Chatterjee: the reason for using wildcards is flexibility. By declaring that a parameter is of type Collection<? super Thing> you give the caller more flexibility as she can invoke your method not only with a Collection<Thing> as an argument but also with a Collection<SomeSupertypeOfThing> as an argument.

    Feb 11, 2016 at 13:40

  • 8

    @AZ_ I share your sentiment. If a method do get() from the list, the method would be considered a Consumer<T>, and the list is considered a provider; but the rule of PECS is “from the list’s point of view”, thus ‘extends’ is called for. It should be GEPS: get extends; put super.

    May 4, 2019 at 13:14

644

+500

The principles behind this in computer science is called

  • Covariance: ? extends MyClass,
  • Contravariance: ? super MyClass and
  • Invariance/non-variance: MyClass

The picture below should explain the concept.
Picture courtesy: Andrey Tyukin

Covariance vs Contravariance

9

  • 183

    Hey everyone. I’m Andrey Tyukin, I just wanted to confirm that anoopelias & DaoWen contacted me and obtained my permission to use the sketch, it’s licensed under (CC)-BY-SA. Thx @ Anoop for giving it a second life^^ @Brian Agnew: (on “few votes”): That’s because it’s a sketch for Scala, it uses Scala syntax and assumes declaration-site variance, which is quite different to Java’s weird call-site variance… Maybe I should write a more detailed answer that clearly shows how this sketch applies to Java…

    Jun 15, 2014 at 23:11

  • 4

    This is one of the simplest and clearest explanations for Covariance and Contravariance that I have ever found!

    – cs4r

    May 1, 2017 at 12:35

  • @Andrey Tyukin Hi, I also want to use this image. How can I contact you?

    – slouc

    Jun 2, 2017 at 8:06

  • 3

  • 1

    Can someone explain the use/application of <? super MyClass>. Because you can put MyClass and its subclass objects into it, but if you want to take out stuff from that collection. They can be taken out only as Objects.

    May 21, 2018 at 9:38

82

When dealing with collections, a common rule for selecting between upper or lower bounded wildcards is PECS. credit

PECS (Producer extends and Consumer super)

mnemonic → Get (extend) and Put (Super) principle.

  • This principle states that:

    • Use an extends wildcard when you only get values out of a structure.
    • Use a super wildcard when you only put values into a structure.
    • And don’t use a wildcard when you both get and put.

Example in Java:

class Super {
        Number testCoVariance() {
            return null;
        }
        void testContraVariance(Number parameter) {
        } 
    }
    
    class Sub extends Super {
        @Override
        Integer testCoVariance() {
            return null;
        } //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
        @Override
        void testContraVariance(Integer parameter) {
        } //doesn't support even though Integer is subtype of Number
    }

The Liskov Substitution Principle (LSP) states that “objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”.

Within the type system of a programming language, a typing rule

  • covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic;
  • contravariant if it reverses this ordering;
  • invariant or nonvariant if neither of these applies.

Covariance and contravariance

  • Read-only data types (sources) can be covariant;
  • write-only data types (sinks) can be contravariant.
  • Mutable data types which act as both sources and sinks should be invariant.

To illustrate this general phenomenon, consider the array type. For the type Animal we can make the type Animal[]

  • covariant: a Cat[] is an Animal[];
  • contravariant: an Animal[] is a Cat[];
  • invariant: an Animal[] is not a Cat[] and a Cat[] is not an Animal[].

Java Examples:

Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)

List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime  

more examples

enter image description here Image src

bounded(i.e. heading toward somewhere) wildcard : There are 3 different flavours of wildcards:

  • In-variance/Non-variance: ? or ? extends ObjectUnbounded Wildcard. It stands for the family of all types. Use when you both get and put.
  • Co-variance: ? extends T ( Reign of T descendants) – a wildcard with an upper bound. T is the upper-most class in the inheritance hierarchy. Use an extends wildcard when you only Get values out of a structure.
  • Contra-variance: ? super T ( Reign of T ancestor) – a wildcard with a lower bound. T is the lower-most class in the inheritance hierarchy. Use a super wildcard when you only Put values into a structure.

Note: wildcard ? means zero or one time, represents an unknown type. The wildcard can be used as the type of a parameter, never used as a type argument for a generic method invocation, a generic class instance creation.(i.e. when used wildcard that reference not used in elsewhere in program like we use T)

enter image description here

 import java.util.ArrayList;
import java.util.List;

class Shape { void draw() {}}

class Circle extends Shape {void draw() {}}

class Square extends Shape {void draw() {}}

class Rectangle extends Shape {void draw() {}}

public class Test {

    public static void main(String[] args) {
        //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
        List<? extends Shape> intList5 = new ArrayList<Shape>();
        List<? extends Shape> intList6 = new ArrayList<Cricle>();
        List<? extends Shape> intList7 = new ArrayList<Rectangle>();
        List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.


        //? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
        List<? super Shape> inList5 = new ArrayList<Shape>();
        List<? super Shape> inList6 = new ArrayList<Object>();
        List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.

        //-----------------------------------------------------------
        Circle circle = new Circle();
        Shape shape = circle; // OK. Circle IS-A Shape

        List<Circle> circles = new ArrayList<>();
        List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape

        List<? extends Circle> circles2 = new ArrayList<>();
        List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>


        //-----------------------------------------------------------
        Shape shape2 = new Shape();
        Circle circle2= (Circle) shape2; // OK. with type casting

        List<Shape> shapes3 = new ArrayList<>();
        List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of  List<Shape> even Circle is subetype of Shape

        List<? super Shape> shapes4 = new ArrayList<>();
        List<? super Circle> circles4 = shapes4; //OK.
    }

    
    
    /*
     * Example for an upper bound wildcard (Get values i.e Producer `extends`)
     *
     * */
    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape()); //ERROR
        list.add(new Circle()); // ERROR
        list.add(new Square()); // ERROR
        list.add(new Rectangle()); // ERROR
        Shape shape= list.get(0);//OK so list act as produces only
    /*
     * You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape>
     * You can get an object and know that it will be an Shape
     */
    }
    
    
    /*
     * Example for  a lower bound wildcard (Put values i.e Consumer`super`)
     * */
    public void testContraVariance(List<? super Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape());//OK
        list.add(new Circle());//OK
        list.add(new Square());//OK
        list.add(new Rectangle());//OK
        Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
        Object object= list.get(0); //OK gets an object, but we don't know what kind of Object it is.
        /*
         * You can add a Shape,Circle,Square,Rectangle to a List<? super Shape>
         * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
         */
    }
}

generics and examples

Covariance and contravariance determine compatibility based on types. In either case, variance is a directed relation. Covariance can be translated as “different in the same direction,” or with-different, whereas contravariance means “different in the opposite direction,” or against-different. Covariant and contravariant types are not the same, but there is a correlation between them. The names imply the direction of the correlation.

https://stackoverflow.com/a/54576828/1697099
https://stackoverflow.com/a/64888058/1697099

  • Covariance: accept subtypes (read only i.e. Producer)
  • Contravariance: accept supertypes (write only i.e. Consumer)

5

  • Hey, I just wanted to know what you meant with the last sentense: “If you think my analogy is wrong please update”. Do you mean if it is ethically wrong (which is subjective) or if it is wrong in the context of programming (which is objective: no, it’s not wrong)? I would like to replace it with a more neutral example which is universally acceptable independent of cultural norms and ethical believes; If that is OK with you.

    – Neuron

    Apr 29, 2018 at 6:12

  • at last I could get it. Nice explanation.

    – Oleg Kuts

    Apr 12, 2019 at 12:36

  • 2

    @Premraj, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put., I cannot add element to List<?> or List<? extends Object>, so I don’t understand why it can be Use when you both get and put.

    May 17, 2019 at 4:05

  • 1

    @LiuWenbin_NO. – That part of the answer is misleading. ? – the “unbounded wildcard” – corresponds with the exact opposite of invariance. Please refer to the following documentation: docs.oracle.com/javase/tutorial/java/generics/… which states: In the case where the code needs to access the variable as both an “in” and an “out” variable, do not use a wildcard. (They are using “in” and “out” as synonymous with “get” and “put”). With the exception of null you can’t add to a Collection parameterized with ?.

    – mouselabs

    Apr 16, 2020 at 18:39


  • stackoverflow.com/a/1368212/1697099 for more

    – Premraj

    Jun 17, 2021 at 17:17