Java Generics PECS – Producer Extends Consumer Super

Yesterday, I was going through some java collection APIs and I found two methods primarily used for adding elements into a collection. They both were using generics syntax for taking method arguments. However, first method was using <? super T> where as second method was using <? extends E>. Why?

Let’s look at the complete syntax of both methods first.

This method is responsible for adding all members of collection “c” into another collection where this method is called.

boolean addAll(Collection<? extends E> c);

This method is called for adding “elements” to collection “c”.

public static <T> boolean addAll(Collection<? super T> c, T... elements);

Both seems to be doing simple thing, so why they both have different syntax. Many of us might wonder. In this post, I am trying to demystify the concept around it, which is primarily called PECS (term first coined by Joshua Bloch in his book Effective Java).

Why Generics Wildcards?

In my last post related to java generics, we learned that generics is used for type safety and invariant by nature. A usecase can be list of Integer i.e. List<Integer>. If you declare a list in java like List<Integer>, then java guarantees that it will detect and report you any attempt to insert any non-integer type into above list.

But many times, we face situations where we have to pass a sub-type or super-type of a class as argument in a method for specific purposes. In these cases, we have to use concepts like covariance (narrowing a reference) and contra-variance (widening a reference).

Understanding <? extends T>

This is the first part of PECS i.e. PE (Producer extends). To more relate it to real life terms, let’s use an analogy of a basket of fruits (i.e. collection of fruits). When we pick a fruit from basket, then we want to be sure that we are taking out only fruit only and nothing else; so that we can write generic code like this:

Fruit get = fruits.get(0);

In above case, we need to declare the collection of fruits as List<? extends Fruit>. e.g.

class Fruit {
   @Override
   public String toString() {
      return "I am a Fruit !!";
   }
}

class Apple extends Fruit {
   @Override
   public String toString() {
      return "I am an Apple !!";
   }
}

public class GenericsExamples
{
   public static void main(String[] args)
   {
      //List of apples
      List<Apple> apples = new ArrayList<Apple>();
      apples.add(new Apple());
      
      //We can assign a list of apples to a basket of fruits;
      //because apple is subtype of fruit 
      List<? extends Fruit> basket = apples;
      
      //Here we know that in basket there is nothing but fruit only
      for (Fruit fruit : basket)
      {
         System.out.println(fruit);
      }
      
      //basket.add(new Apple()); //Compile time error
      //basket.add(new Fruit()); //Compile time error
   }
}

Look at the for loop above. It ensures that whatever it comes out from basket is definitely going to be a fruit; so you iterate over it and simply cast it a Fruit. Now in last two lines, I tried to add an Apple and then a Fruit in basket, but compiler didn’t allowed me. Why?

The reason is pretty simple, if we think about it; the <? extends Fruit> wildcard tells the compiler that we’re dealing with a subtype of the type Fruit, but we cannot know which fruit as there may be multiple subtypes. Since there’s no way to tell, and we need to guarantee type safety (invariance), you won’t be allowed to put anything inside such a structure.

On the other hand, since we know that whichever type it might be, it will be a subtype of Fruit, we can get data out of the structure with the guarantee that it will be a Fruit.

In above example, we are taking elements out of collection “List<? extends Fruit> basket“; so here this basket is actually producing the elements i.e. fruits. In simple words, when you want to ONLY retrieve elements out of a collection, treat it as a producer and use “? extends T>” syntax. “Producer extends” now should make more sense to you.

Understanding <? super T>

Now look at above usecase in different way. Let’s assume we are defining a method where we will only be adding different fruits inside this basket. Just like we saw the method in start of post “addAll(Collection<? super T> c, T... elements)“. In such case, basket is used for storing the elements so it should be called consumer of elements.

Now look at the code example below:

class Fruit {
   @Override
   public String toString() {
      return "I am a Fruit !!";
   }
}

class Apple extends Fruit {
   @Override
   public String toString() {
      return "I am an Apple !!";
   }
}

class AsianApple extends Apple {
   @Override
   public String toString() {
      return "I am an AsianApple !!";
   }
}

public class GenericsExamples
{
   public static void main(String[] args)
   {
      //List of apples
      List<Apple> apples = new ArrayList<Apple>();
      apples.add(new Apple());
      
      //We can assign a list of apples to a basket of apples
      List<? super Apple> basket = apples;
      
      basket.add(new Apple()); 		//Successful
      basket.add(new AsianApple()); //Successful
      basket.add(new Fruit()); 		//Compile time error
   }
}

We are able to add apple and even Asian apple inside basket, but we are not able to add Fruit (super type of apple) to basket. Why?

Reason is that basket is a reference to a List of something that is a supertype of Apple. Again, we cannot know which supertype it is, but we know that Apple and any of its subtypes (which are subtype of Fruit) can be added to be without problem (you can always add a subtype in collection of supertype). So, now we can add any type of Apple inside basket.

What about getting data out of such a type? It turns out that you the only thing you can get out of it will be Object instances: since we cannot know which supertype it is, the compiler can only guarantee that it will be a reference to an Object, since Object is the supertype of any Java type.

In above example, we are putting elements inside collection “List<? super Apple> basket“; so here this basket is actually consuming the elements i.e. apples. In simple words, when you want to ONLY add elements inside a collection, treat it as a consumer and use “? super T>” syntax. Now, “Consumer super” also should make more sense to you.

Summary

Based on above reasoning and examples, let’s summarize our learning in bullet points.

  1. Use the <? extends T> wildcard if you need to retrieve object of type T from a collection.
  2. Use the <? super T> wildcard if you need to put objects of type T in a collection.
  3. If you need to satisfy both things, well, don’t use any wildcard. As simple as it is.
  4. In short, remember the term PECS. Producer extends Consumer super. Really easy to remember.

That’s all for simple yet complex concept in generics in java. Let me know of your thoughts via comments.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

7 thoughts on “Java Generics PECS – Producer Extends Consumer Super”

  1. In first example, we are using extends keyword. since we extends for subtyping so why can’t we put apple which is a subtype of fruits ?.

    Reply
    • Because a reference to List could point to a List or a List or a List or any number of fruit types.

      In this case we know it’s a List, so adding an apple would be fine, but in general we don’t know what the actual type of the list is, just that it contains some kind of Fruit. If you were able to add a Banana to a List, then if the actual list was of type List then your list of AsianApples would suddenly contain a Banana! You would have circumvented the type system.

      Reply
      • Because a reference to

        List<? extends Fruit>

        could point to a

        List<AsianApple>

        or a

        List<Fruit>

        or a

        List<Apple>

        or any number of fruit types.

        In this case we know it’s a

        List<Apple>

        , so adding an apple would be fine, but in general we don’t know what the actual type of the list is, just that it contains some kind of Fruit. If you were able to add a Banana to a

        List<? extends Fruit>

        , then if the actual list was of type

        List<AsianApple>

        then your list of AsianApples would suddenly contain a Banana! You would have circumvented the type system.

        Reply
  2. Hi Lokesh,

    I have a situation in which I need to make sure that only SubType elements are going to be accepted in a List.

    Say I have ClassA which is getting extended by ClassB,ClassC and I want to declare a List, How I can achieve that ?

    Thanks in advance.

    Reply
  3. Hi Lokesh, thank you for the post, I wonder how would one write non-generic code for the same above discussed scenario. I feel it would be even more helpful to understand the distinction if you could explain the same examples with non-generic code.

    Reply
    • Generics in java are present to provide type safety during compile time i.e. writing the code. Once you have compiled the code, there is no extra bit added to bytecode related to generics, and it’s completely equal to raw code. So if you are not using generics, then every collection is going to be equal only ArrayList of Objects, where you can put anything and get anything out of it. It’s coder’s responsibility to write code in such a way that no ClassCastExceptions are thrown at runtime. It means you have to take care of every collection in application, and remember in which collection what are the value type.

      Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.