Lesson 20 โข Advanced
Advanced Generics: Wildcards & Type Erasure
Upper/lower bounds, ? wildcards, and how type erasure affects runtime behavior.
๐ Before You Start
You should understand:
- Generics (Lesson 14) โ Box<T>, type parameters, bounds
- Collections (Lesson 13) โ ArrayList, HashMap usage
- Inheritance (Lesson 10) โ class hierarchies and polymorphism
What You'll Learn
- โ
Upper bounded wildcards:
? extends T - โ
Lower bounded wildcards:
? super T - โ
Unbounded wildcards:
? - โ PECS principle (Producer Extends, Consumer Super)
- โ Type erasure and its implications
- โ Generic method type inference
1๏ธโฃ Understanding Wildcards
๐ก Analogy: Delivery Boxes โ ? extends Fruit is a "read-only fruit box" โ you can take items out (they're at least Fruit), but can't put items in (is it an Apple box? Orange box?). ? super Apple is a "write-only apple-compatible box" โ you can put Apples in, but when reading you only know it's Object.
PECS: The Golden Rule โ Producer Extends, Consumer Super. Reading from a collection โ use extends. Writing to a collection โ use super.
// PRODUCER โ reads from the list
public double sum(List<? extends Number> numbers) {
double total = 0;
for (Number n : numbers) total += n.doubleValue();
return total;
}
// Works with List<Integer>, List<Double>, List<Number>
// CONSUMER โ writes to the list
public void addIntegers(List<? super Integer> list) {
list.add(1); list.add(2);
}
// Works with List<Integer>, List<Number>, List<Object>Try It: Wildcards & PECS in Action
See how upper and lower bounded wildcards control read/write access
// ๐ก Try modifying this code and see what happens!
// Wildcards & PECS (Simulated)
console.log("=== Wildcards ===\n");
// 1. Upper bounded (? extends Number) โ READ only
console.log("1. UPPER BOUNDED (? extends Number):");
console.log(" Can READ but not WRITE\n");
function sumOfList(numbers) {
return numbers.reduce((sum, n) => sum + n, 0);
}
let integers = [1, 2, 3, 4, 5];
let doubles = [1.5, 2.5, 3.5];
console.log(" Sum of integers:", sumOfList(integers));
console.log(" Sum of doubl
...2๏ธโฃ Type Erasure: Why It Matters
At compile time, Java checks generics rigorously. But at runtime, all generic type information is erased โ List<String> becomes just List.
Consequences:
- โ
new T()โ can't create generic instances - โ
instanceof List<String>โ can't check generic types - โ
new T[10]โ can't create generic arrays - โ
Class<T>token โ pass type info explicitly
3๏ธโฃ Generic Method Type Inference
Java can often infer type parameters automatically. The diamond operator <> (Java 7+) lets the compiler figure out the types:
// Compiler infers T = String
List<String> list = new ArrayList<>();
// Generic method โ compiler infers types from arguments
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
// Infers T = Integer
int m = max(3, 7); // 7Try It: Type Inference & Bounded Methods
Write generic utility functions with type inference and bounds
// ๐ก Try modifying this code and see what happens!
// Type Inference & Bounded Methods (Simulated)
console.log("=== Generic Method Inference ===\n");
// 1. findMax with comparator
function findMax(arr, comparator) {
if (arr.length === 0) return null;
let max = arr[0];
for (let item of arr) {
if (comparator(item, max) > 0) max = item;
}
return max;
}
console.log("1. findMax with different types:");
console.log(" Max number:", findMax([3, 1, 4, 1, 5, 9], (a, b) => a
...4๏ธโฃ Advanced Wildcard Patterns
Collections.copy(): The best real-world PECS example โ source is ? extends T (producer) and dest is ? super T (consumer):
// Java Collections.copy signature:
public static <T> void copy(
List<? super T> dest, // CONSUMER โ writes
List<? extends T> src // PRODUCER โ reads
)Try It: Build Generic Utility Functions
Create a type-safe utility library with generic methods
// ๐ก Try modifying this code and see what happens!
// Generic Utility Library (Simulated)
console.log("=== Generic Utilities ===\n");
// 1. Generic zip (combine two arrays into pairs)
function zip(a, b) {
let result = [];
let len = Math.min(a.length, b.length);
for (let i = 0; i < len; i++) result.push([a[i], b[i]]);
return result;
}
console.log("1. Zip:");
let names = ["Alice", "Bob", "Charlie"];
let scores = [95, 87, 92];
zip(names, scores).forEach(([name, score]) => {
c
...5๏ธโฃ Common Mistakes
List list = new ArrayList(); bypasses all safety. Always parameterize.List<? extends Animal> for covariance.new T[10] doesn't compile. Use List<T> instead.6๏ธโฃ Pro Tips
๐ก When in doubt, use PECS. Reading โ ? extends. Writing โ ? super. Both โ concrete type.
๐ก Diamond operator <> (Java 7+) lets the compiler infer: List<String> list = new ArrayList<>();
๐ก Study Collections.copy() โ it's the canonical PECS example in the JDK.
๐ Quick Reference
| Wildcard | Meaning | Read/Write |
|---|---|---|
| ? extends T | T or its subtypes | Read โ Write โ |
| ? super T | T or its supertypes | Read โ Write โ |
| ? | Any type | Read as Object only |
| T extends Comparable | T must be Comparable | Full access |
๐ Lesson Complete!
You now understand advanced generics including wildcards, PECS, and type erasure!
Next: Streams API โ build complex data pipelines with filter, map, collect, and flatMap.
Sign up for free to track which lessons you've completed and get learning reminders.