Java Generics provide flexibility and type safety when dealing with collections and data structures. One of the key ideas within the same is Java Generics PECS, short for “Producer Extends, Consumer Super.” Introduced by Joshua Bloch, this rule helps developers in identifying the most suitable wildcard when dealing with generic types. This article discusses Java Generics PECS in detail, along with associated topics such as covariance, contravariance, and invariance.
Table of Contents:
What are Java Generics?
A class, interface, or method that works on a typed data structure is called a generic entity.
In Java, generics allow you to create classes, interfaces, and methods that can work with different types of data while maintaining type safety at compile time, eliminating the need for casting and improving code readability.
The Object is the parent class of all other classes, and its reference can be used to point to any other object. These types of objects are not type-safe. Hence, generics introduce a type of safety by using a specific data type.
Example:
Here, without generics, collections will store Object references, which is not type-safe and may lead to ClassCastException at runtime.
Java Generics Wildcards
In generic code, the question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used for a variety of situations, such as the type of a parameter, field, or local variable. Unlike arrays, different objects of a generic type are not suitable with each other, not even explicitly. This can be solved by the wildcard if it is used as an actual type parameter.
Wildcards come in three forms:
- ? (unbounded): Unknown type without restrictions
- ? extends T: Upper-bounded wildcard
- ? super T: Lower-bounded wildcard
Before we dive into Java Generics PECS, it is important to learn the concept of Covariance, Contravariance, and Invariance in detail.
Java Covariance, Contravariance, and Invariance
In Java Generics, these concepts tell us how the data types are related to each other when they are using the wildcards.
1. Covariance (? extends T)
Covariance allows a generic type to be a subtype of the other generic type, i.e., a List<? extends Number> can hold an Integer, Double, or Float value, and it is read-only, meaning that no new elements can be added.
Read-only: You can read elements as type T, but you cannot add new elements (except null).
List<? extends Number> numbers = new ArrayList<Integer>();
Number num = numbers.get(0); // Allowed
// numbers.add(10); // Not allowed
2. Contravariance (? super T)
Contravariance allows a generic type to be a supertype of another generic type, i.e., a List<? super Integer> can hold an Integer, Number, or an Object value, allowing the elements to be added but taking care of the safe retrieval, i.e., an Object.
Write-safe: You can add T or its subtypes, but reading only returns Object.
List<? super Integer> numbers = new ArrayList<Number>();
numbers.add(10); // Allowed
Object obj = numbers.get(0); // Reading results in Object
3. Invariance (No Wildcards)
Invariance means that a generic type must match exactly, so a List<Number> can only store the Number and not its subtypes like Integer. These rules take care of safety and give flexibility.
List<Number> nums = new ArrayList<>();
nums.add(1); // Allowed
// List<Integer> ints = nums; // Not allowed
Now, that you are aware of how wildcards and how to use the above concept in deciding the datatype, let us move forward. These rules form the foundation for the Java Generics PECS rule.
What is Java Generics PECS?
The concept of PECS is broken into two parts:
1. Producer Extends: If a collection produces data for you, use ? extends T.
2. Consumer Super: If a collection only consumes items from you, use <? super T>
PECS Producer Extends, Consumer Super is a principle in generics in Java that helps in deciding which one to use, extends, or super, when dealing with the wildcard types (?).
It was introduced by Joshua Bloch to make the generic code more flexible and secure.
For example, the Mammal class extends the Animal class (the Animal class is a superclass of the Mammal class). The Cat/Dog class extends the Mammal class (the Mammal class is a superclass of the Cat/Dog class).
Producer Extends
The <? extends T> wildcard is used when you only want to produce items from a structure (collection). This means that you will only read items from the structure and never write to it. The extends keyword is used to specify that the wildcard represents a subtype of T.
Example
List<? extends Number> numbers = new ArrayList<Double>();
Number num = numbers.get(0); // Allowed (reading)
numbers.add(10); //writing not allowed
Consumer Super
The <? super T> wildcard is used when you only want to consume items from a structure. This means that you will only write items to the structure and never read from it. The super keyword is used to specify that the wildcard represents a supertype of T.
Example
List<? super Integer> numbers = new ArrayList<Number>();
numbers.add(10); //Allowed (writing)
Integer num = numbers.get(0); // reading is not safe
Get 100% Hike!
Master Most in Demand Skills Now!
Producing and Consuming
When a collection produces and consumes elements at the same time, PECS (Producer Extends, Consumer Super) does not apply there. Instead, you should use an exact generic type (T) without wildcards, which will allow both reading and writing.
Example: Modifying a List
Output:
Explanation: The above code is showing both producing (reading) and consuming (writing) in a list. The modifyList method is reading each item from the list and printing it. Then, it is adding a new item to the list. Since the method is doing both reading and writing, it is using List<T> without wildcards.
Real-World Analogy for Java Generics PECS
Imagine you have two water containers: a larger one and a smaller one.
You cannot pour water from a larger container into a smaller one without spilling.
You CAN ONLY pour water from a smaller container into a bigger one.
So,
- When you use <? super SomeType>, you are describing a container that is the same size or larger
- When you use <? extends SomeType>, you are describing a container that is the same size or smaller
Now, you can create it by thinking of Producers and Consumers like this:
A ‘Producer’ is like a container we only pour water from. i.e., we can take water from it, but we cannot add more because we don’t know its exact type
A ‘Consumer’ is like a container we only pour water into. i.e., we can pour water into it, but we don’t know exactly what’s inside, so we must be careful
When to Avoid Wildcards
If a method both reads and writes to a collection, don’t use wildcards. Instead use an exact generic type.
Example:
public static <T> void modifyList(List<T> list, T newItem) {
for (T item : list) { // Producing
System.out.println(item);
}
list.add(newItem); // Consuming
}
This code above allows both reading and writing without types issues.
Free Online Java Certification Course
This self-paced Java course will help you develop a solid foundation and job-ready skills in Java development.
Useful Resources
Conclusion
Java Generics PECS is an important concept in generics that helps to ensure safety when working with wildcard types. Producers can only be used for reading, while Consumers can only be used for writing. If both reading and writing are needed, a generic type (T) without wildcards should be used. This makes generic code more flexible and secure, helping Java collections become more efficient and type-safe.
Master generics in Java with our hands-on course and sharpen your skills with curated interview questions. Start learning today to boost your coding confidence and ace your next Java interview.
Java Generics PECS – FAQs
Q1. What does PECS stand for in Java Generics?
PECS means Producer Extends, Consumer Super, a rule for deciding which wildcard to use in generics.
Q2. When should I use extends vs super in Java?
Use extends when you only read from the collection, super when you only write to it.
Q3. What is covariance in Java?
Covariance (? extends T) allows reading subtypes of T from a collection.
Q4. What is contravariance in Java?
Contravariance (? super T) allows adding subtypes of T to a collection.
Q5. Can I use wildcards when both reading and writing?
No, in such cases use an exact generic type (List).