Table of Contents#
- Understanding Java Generics Basics
- Wildcards in Generics:
extendsvs.super - Deep Dive:
<? super T>(Lower Bounded Wildcards) - HashMap’s
put()Method and Generic Types - Why
new Object()Fails inHashMap<? super ArrayList>.put() - Practical Examples and Common Pitfalls
- Conclusion
- References
1. Understanding Java Generics Basics#
Before diving into wildcards, let’s recap core generics concepts:
What Are Generics?#
Generics allow classes, interfaces, and methods to operate with type parameters (e.g., List<String>, HashMap<K, V>). They enforce type safety by ensuring a collection holds only one type of object, eliminating runtime ClassCastException errors.
Raw Types vs. Parameterized Types#
- A raw type (e.g.,
List) ignores generics, allowing any object type (unsafe). - A parameterized type (e.g.,
List<String>) specifies the type of objects it contains (safe).
2. Wildcards in Generics: extends vs. super#
Wildcards (?) represent unknown types in generics. They enable flexibility when working with generic classes but come with rules for type safety. There are two key wildcard bounds:
Upper Bounded Wildcards: <? extends T>#
Represents an unknown type that is a subtype of T (or T itself). For example:
List<? extends Number> numbers; // Can hold Integer, Double, Number, etc.Rule: Use extends for producers (collections you read from). You can retrieve elements as T but cannot add elements (except null), because the actual type could be a subtype of T (e.g., List<Integer>), and adding a Double would break type safety.
Lower Bounded Wildcards: <? super T>#
Represents an unknown type that is a supertype of T (or T itself). For example:
List<? super Integer> integers; // Can hold Integer, Number, Object, etc.Rule: Use super for consumers (collections you write to). You can add elements of type T (or its subtypes), but retrieving elements returns Object (since the actual type could be a supertype of T).
The PECS Mnemonic#
To remember when to use extends vs. super, use PECS:
- Producer: Extends (read from)
- Consumer: Super (write to)
3. Deep Dive: <? super T> (Lower Bounded Wildcards)#
For <? super T>, the unknown type is a supertype of T. For example, <? super ArrayList> could represent:
ArrayList(itself),List(supertype ofArrayList),Collection(supertype ofList),Object(supertype of all objects).
But how does this affect what we can add to a collection?
4. HashMap’s put() Method and Generic Types#
HashMap is a parameterized class with two type parameters: K (key) and V (value). Its put() method adds a key-value pair:
V put(K key, V value); // Adds key-value pair to the mapWhen we write HashMap<? super ArrayList>, we’re using a wildcard for one of its type parameters (either K or V). For clarity, let’s assume we’re focusing on the value type (e.g., HashMap<String, ? super ArrayList>), where the value is bounded by <? super ArrayList>.
5. Why new Object() Fails in HashMap<? super ArrayList>.put()#
Let’s formalize the problem: Suppose we have a HashMap where the value type is <? super ArrayList>:
HashMap<String, ? super ArrayList> listMap = new HashMap<>();We try to add an Object as a value:
listMap.put("key", new Object()); // Compile error! Why?The Root Cause: Type Safety#
<? super ArrayList> means the value type is an unknown supertype of ArrayList (let’s call this unknown type X). X could be:
ArrayList,List,Collection,Object.
For put() to be safe, the value we add must be compatible with any possible X.
Why new Object() Isn’t Compatible#
Object is a supertype of ArrayList, but X might not be Object. For example:
- If
XisArrayList(a supertype ofArrayList), the map expectsArrayListvalues. AddingObjectwould violate type safety (anObjectis not anArrayList). - If
XisList, the map expectsListvalues.Objectis not aList, so adding it would fail.
What Can We Add?#
With <? super T>, you can safely add instances of T or its subtypes. For <? super ArrayList>, this includes:
ArrayList(itself),- Any subclass of
ArrayList(e.g.,CustomArrayList extends ArrayList).
These are guaranteed to be subtypes of any possible X (since X is a supertype of ArrayList). For example:
listMap.put("key", new ArrayList<>()); // OK: ArrayList is a subtype of X
listMap.put("key", new CustomArrayList()); // OK: Subtype of ArrayList (and thus X)6. Practical Examples and Common Pitfalls#
Example 1: List<? super ArrayList>#
Let’s test with a List first (simpler than HashMap):
List<? super ArrayList> list = new ArrayList<>();
// Allowed: Add ArrayList or its subtypes
list.add(new ArrayList<>()); // OK
list.add(new CustomArrayList()); // OK (if CustomArrayList extends ArrayList)
// Rejected: Add supertypes of ArrayList
list.add(new Object()); // Compile error!
list.add(new List() {}); // Compile error! (List is a supertype of ArrayList)Example 2: HashMap<String, ? super ArrayList>#
Now apply this to HashMap, where the value type is <? super ArrayList>:
HashMap<String, ? super ArrayList> map = new HashMap<>();
// Allowed: Add ArrayList or subtypes as values
map.put("a", new ArrayList<>()); // OK
map.put("b", new CustomArrayList()); // OK
// Rejected: Add Object (supertype of ArrayList)
map.put("c", new Object()); // Compile error!Common Pitfall: Confusing super with "Any Supertype"#
Developers often assume <? super T> allows any supertype of T, but this is incorrect. super enforces that added elements are subtypes of the unknown X (the supertype of T). Since X could be T itself, only T and its subtypes are safe to add.
7. Conclusion#
The key takeaway is understanding the super wildcard rule:
<? super T>represents an unknown supertype ofT.- You can add
Tor its subtypes to a consumer collection (PECS: Consumer Super). - You cannot add supertypes of
T(e.g.,Objectfor<? super ArrayList>), because the actual typeXmight be a subtype of that supertype, breaking type safety.
Thus, HashMap<? super ArrayList> rejects new Object() in put() because Object is a supertype of ArrayList, and the compiler cannot guarantee it matches the unknown supertype X.
8. References#
- Java Generics Tutorial (Oracle)
- Effective Java (3rd Ed.) by Joshua Bloch (Item 31: Use bounded wildcards to increase API flexibility)
- Java PECS Rule (Stack Overflow)
By mastering wildcards and PECS, you’ll write safer, more flexible generic code. Remember: extends for reading, super for writing! 🚀