🏆 What Are Wildcards (?) in Java?
Wildcards allow you to use unknown types in generics. They are useful when working with collections of different but related types.
Types of Wildcards in Java
| Wildcard | Meaning | Example |
|---|---|---|
? (Unbounded) | Accepts any type | List<?> list = new ArrayList<String>(); |
? extends T (Upper Bounded) | Accepts T or any subclass of T | List<? extends Number> list = new ArrayList<Integer>(); |
? super T (Lower Bounded) | Accepts T or any superclass of T | List<? super Integer> list = new ArrayList<Number>(); |
✅ 1. Unbounded Wildcard (?)
- Use Case: When the type doesn’t matter (read-only operations).
- Example:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
List<String> names = List.of("Alice", "Bob");
List<Integer> numbers = List.of(1, 2, 3);
printList(names); // Works
printList(numbers); // Works
Rules:
- You can’t add elements to a
List<?>(exceptnull) because the type is unknown. - Only reading is allowed.
✅ 2. Upper Bounded Wildcard (? extends T)
- Use Case: When you want to accept
Tor any of its subclasses but ensure safe reading. - Example:
public static double sumList(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue(); // Safe: We know it’s at least a Number
}
return sum;
}
List<Integer> intList = List.of(1, 2, 3);
List<Double> doubleList = List.of(1.5, 2.5, 3.5);
System.out.println(sumList(intList)); // Works
System.out.println(sumList(doubleList)); // Works
Rules:
- ✅ Reading is allowed (
Number num = list.get(i);) - ❌ Writing is NOT allowed (
list.add(10);won’t compile!)
✅ 3. Lower Bounded Wildcard (? super T)
- Use Case: When you want to accept
Tor any of its superclasses, ensuring safe writing. - Example:
public static void addNumbers(List<? super Integer> list) {
list.add(10); // Allowed
list.add(20); // Allowed
// list.add(3.14); // ERROR: Only Integers or their subclasses allowed
}
List<Integer> intList = new ArrayList<>();
List<Number> numList = new ArrayList<>();
addNumbers(intList); // Works
addNumbers(numList); // Works
Rules:
- ✅ Writing is allowed (
list.add(new Integer(10));) - ❌ Reading is limited (
Object obj = list.get(i);but NOTInteger num = list.get(i);)
🤔 When to Use Which Wildcard?
| Use Case | Wildcard to Use |
|---|---|
| Read-only access to unknown type | ? (Unbounded) |
| Reading from a list of unknown subtypes | ? extends T (Upper Bounded) |
| Writing to a list of unknown supertypes | ? super T (Lower Bounded) |
🔥 Quick Mnemonic:
- Producer (
extends) — Use? extends Tfor reading.- “If it produces, it extends.”
- Example:
List<? extends Number> list→ You can read Numbers, but not add anything.
- Consumer (
super) — Use? super Tfor writing.- “If it consumes, it super.”
- Example:
List<? super Integer> list→ You can add Integers, but only read Objects.
🏁 Final Thoughts
Wildcards make Java generics more flexible while keeping type safety. They help when working with collections of different but related types and let you enforce safe read/write access.
Would you like to see more real-world examples or need further clarifications? 😊