Wildcards
Wildcards (?) let generic code accept a family of related parameterized types, expressing flexible read-only or write-only relationships that a fixed type parameter can't.
A wildcard, written ?, represents an unknown type in a generic type usage. Unlike a type parameter (<T>), a wildcard isn't declared and reused across a method body — it's used at the point where a generic type is referenced, to say "this can be a List of anything" (List<?>) or "a List of some subtype of Number" (List<? extends Number>).
The Problem Wildcards Solve
Generics are invariant: List<Integer> is not a subtype of List<Number>, even though Integer is a subtype of Number. A method that takes List<Number> cannot accept a List<Integer> argument, which is surprisingly restrictive for read-only operations.
This restriction exists because List<Number> allows adding any Number (e.g. a Double) — if List<Integer> were accepted as-is, that add() could silently corrupt what the caller thinks is an all-Integer list. Wildcards solve this safely.
public static double sum(List<Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue();
return total;
}
List<Integer> ints = List.of(1, 2, 3);
// sum(ints); // compile-time error: List<Integer> is not a List<Number>The Unbounded Wildcard: ?
List<?> means "a List of some specific but unknown type." It's useful when a method only needs capabilities every List has regardless of element type, such as size() or reading elements as Object.
public static void printSize(List<?> list) {
System.out.println("Size: " + list.size());
for (Object o : list) {
System.out.println(o); // elements are only known to be Object
}
}
printSize(List.of("a", "b")); // works for List<String>
printSize(List.of(1, 2, 3)); // works for List<Integer>Upper-Bounded Wildcard: ? extends T
List<? extends Number> means "a List of some unknown type that is Number or a subtype of Number." This is exactly the flexibility the sum() example above needed.
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue(); // safe: every element IS-A Number
return total;
}
List<Integer> ints = List.of(1, 2, 3);
List<Double> doubles = List.of(1.5, 2.5);
sum(ints); // OK now
sum(doubles); // OKLower-Bounded Wildcard: ? super T
List<? super Integer> means "a List of some unknown type that is Integer or a supertype of Integer." This is used when you need to write Integers into the list safely, even though you don't know its exact element type.
public static void addNumbers(List<? super Integer> list) {
list.add(1); // safe: any supertype of Integer can hold an Integer
list.add(2);
}
List<Integer> ints = new ArrayList<>();
List<Number> nums = new ArrayList<>();
List<Object> objs = new ArrayList<>();
addNumbers(ints); // OK
addNumbers(nums); // OK: Number is a supertype of Integer
addNumbers(objs); // OK: Object is a supertype of IntegerPECS: Producer Extends, Consumer Super
PECS is the standard mnemonic for choosing between extends and super: if a parameterized type only produces (you read from it), use extends; if it only consumes (you write to it), use super. If it does both, use neither wildcard — use an exact type parameter instead.
PECS — Producer Extends, Consumer Super
List<? extends T> -- a PRODUCER of T: you can safely read T (or a subtype) out of it
you CANNOT safely add to it (except null)
List<? super T> -- a CONSUMER of T: you can safely add T into it
reading from it only guarantees Object
Read-only from the structure -> use "? extends T"
Write-only into the structure -> use "? super T"
Both read and write -> use a plain type parameter T, no wildcard// A real JDK example following PECS: Collections.copy
// static <T> void copy(List<? super T> dest, List<? extends T> src)
// src PRODUCES elements (read from) -> extends
// dest CONSUMES elements (written to) -> superReading and Writing Behavior with Wildcards
The compiler enforces PECS mechanically: ? extends T forbids add() (except adding null, which is type-safe for any reference type) because the compiler can't guarantee what exact subtype the list really holds; ? super T allows adding T but only lets you read elements out as Object, because the compiler doesn't know exactly how far "up" the supertype chain the real list's type sits.
List<? extends Number> producers = List.of(1, 2, 3);
Number n = producers.get(0); // OK: every element IS-A Number
// producers.add(5); // compile-time error: can't guarantee safety
List<? super Integer> consumers = new ArrayList<Number>();
consumers.add(5); // OK: Integer fits any supertype of Integer
Object o = consumers.get(0); // only guaranteed to be Object, not IntegerWildcard Capture (Conceptual)
Sometimes the compiler needs to "remember" that a wildcard, though unknown, refers to one specific fixed type for the duration of a single method call — this is called wildcard capture. It typically happens invisibly, letting certain operations compile that look like they shouldn't; it can also cause a confusing compile error when you try to write your own helper for such a case without a type parameter to "capture" the wildcard into.
The private generic helper 'captures' the unknown wildcard type as a concrete type parameter T for the duration of the call, which the compiler can then reason about consistently.
public static void swapFirstTwo(List<?> list) {
// list.set(0, list.get(1)); // compile-time error: compiler won't trust ? as consistent
swapHelper(list); // delegate to a private generic method that captures the wildcard
}
private static <T> void swapHelper(List<T> list) {
T temp = list.get(0);
list.set(0, list.get(1)); // OK here: T is a single, fixed, known-to-be-consistent type
list.set(1, temp);
}Wildcards vs. Type Parameters: When to Use Which
Use a wildcard in a method parameter type when the method doesn't need to relate the element type to anything else in its signature. Use a type parameter when the method needs to refer to the same type more than once (e.g. taking a value of that type as another argument, or returning it).
// Wildcard: fine, because the element type isn't reused elsewhere
static void printAll(List<?> list) { for (Object o : list) System.out.println(o); }
// Type parameter needed: the method must relate the list's element type
// to the value being added, so a wildcard alone can't express this
static <T> void addIfAbsent(List<T> list, T value) {
if (!list.contains(value)) list.add(value);
}Practical Usage
Wildcards are common in method parameter types throughout the JDK: Collections.max(Collection<? extends T> coll), Collections.copy(List<? super T> dest, List<? extends T> src), and event-listener style APIs that only ever read or only ever write.
public static double totalArea(List<? extends Shape> shapes) {
double total = 0;
for (Shape s : shapes) total += s.area();
return total;
}
interface Shape { double area(); }
class Circle implements Shape {
double r;
Circle(double r) { this.r = r; }
public double area() { return Math.PI * r * r; }
}
class Square implements Shape {
double side;
Square(double side) { this.side = side; }
public double area() { return side * side; }
}
// totalArea can accept List<Circle>, List<Square>, or List<Shape> alikeEdge Cases
Two independently declared wildcards, even both ?, are not guaranteed to represent the same unknown type, which is why List<?> a and List<?> b cannot be assumed compatible for operations that would require them to share an element type.
List<Object> and List<?> are different: List<Object> is a specific parameterized type that accepts adding any Object; List<?> is a family of types where the specific element type is simply unknown, so you cannot add arbitrary Objects to it.
List<?> a = List.of(1, 2);
List<?> b = List.of("x", "y");
// The compiler treats a's ? and b's ? as potentially different unknown types,
// even though both declarations use the same "?" symbol.Mental Model
Think of ? extends T as a locked box you're only allowed to look inside (safe to read as T, unsafe to insert), and ? super T as a mailbox you're only allowed to drop letters into (safe to insert T, unsafe to assume what you'll pull out beyond Object). PECS — Producer Extends, Consumer Super — captures exactly this intuition.
Quick Reference
List<?> unknown type, read as Object only, cannot add (except null)
List<? extends T> unknown subtype of T, read as T, cannot add (except null) — PRODUCER
List<? super T> unknown supertype of T, can add T, read as Object only — CONSUMER
PECS: Producer Extends, Consumer Super
Both read+write needed -> use a plain type parameter <T>, not a wildcardKey Takeaways
A wildcard ? represents an unknown type in a specific use of a generic type, unlike a declared, reusable type parameter.
List<? extends T> is a producer: safe to read as T, unsafe to add to (except null).
List<? super T> is a consumer: safe to add T to, only guaranteed to read back as Object.
PECS — Producer Extends, Consumer Super — is the standard rule for choosing between the two bounded wildcard forms.
When a method needs to both read and write, or needs to relate the element type to another parameter or the return type, use a plain type parameter instead of a wildcard.
public static double sum(List<Number> list) {... } List<Integer> ints = List.of(1, 2, 3); sum(ints); // does not compile: List<Integer> is not a List<Number> public static double sum(List<? extends Number> list) {... } sum(ints); // OK: accepts any List of Number or a subtype
Generics are invariant, so List<Integer> is not assignable where List<Number> is required; an upper-bounded wildcard (? extends Number) is the correct way to accept any list of Number-or-subtype elements read-only.
List<? extends Number> list = new ArrayList<Integer>(); list.add(5); // does not compile
List<? super Integer> list = new ArrayList<Number>(); list.add(5); // OK — a consumer wildcard permits adding
An extends wildcard is a read-only producer view; the compiler can't verify it's safe to add to it because the list's real element type might be a narrower subtype than what you're trying to add. Use a super wildcard when the method needs to add elements.
List<?> list = List.of(1, 2, 3); Integer first = list.get(0); // does not compile: get() only guarantees Object
List<?> list = List.of(1, 2, 3); Object first = list.get(0); // OK — unbounded wildcard only guarantees Object
An unbounded wildcard List<?> gives no information about the element type beyond Object; retrieving an element and assigning it directly to a more specific type without a cast does not compile.
Why is List<Integer> not accepted where a List<Number> parameter is expected, even though Integer extends Number?
Java generics are invariant: List<Integer> is not a subtype of List<Number>. If it were allowed, code could add a non-Integer Number (like a Double) into what the caller believes is a List<Integer>, silently corrupting it. Wildcards (? extends Number) are the safe way to express the desired flexibility.
What does List<? extends Number> allow you to do, and what does it forbid?
It allows you to read elements out and treat them as Number (since every element IS-A Number, whatever the real subtype is). It forbids adding elements (except null), because the compiler cannot verify the real underlying list's exact element type would accept whatever you try to add.
What does List<? super Integer> allow you to do, and what does it forbid?
It allows you to add Integer values into the list (since any supertype of Integer can legally hold an Integer). It forbids treating retrieved elements as anything more specific than Object, since the compiler only knows the real type is Integer or some ancestor of it.
State the PECS rule and explain what it stands for.
PECS stands for "Producer Extends, Consumer Super": use ? extends T for a parameter you only read from (a producer of T), and use ? super T for a parameter you only write to (a consumer of T).
When should you use a plain type parameter <T> instead of either wildcard form?
When the method needs to both read and write the same generic type, or needs to relate the type across multiple parameters or a return value — a wildcard by itself can't express that kind of dependency between positions.
Interview: Why does the compiler forbid adding to a List<? extends Number> even though every real list must have some concrete, addable element type?
The compiler doesn't know at compile time whether the real underlying type is List<Integer>, List<Double>, or some other Number subtype; allowing add() would risk inserting, say, a Double into what's actually a List<Integer>, so the compiler conservatively forbids all inserts except the universally-safe null.
Interview: Are List<?> and List<Object> interchangeable? Why or why not?
No. List<Object> is a specific parameterized type whose declared element type is Object, and you can legally add any Object to it. List<?> represents "a List of some specific but unknown type," which could be List<String>, List<Integer>, etc. — you cannot add arbitrary Objects to it because the real element type might not accept them.
Write a method printAll(List<?> list) that prints every element of any list, regardless of its element type.
public static void printAll(List<?> list) { for (Object o : list) { System.out.println(o); } }
The unbounded wildcard List<?> accepts a list of any element type; since the method only reads elements as Object (via println's toString handling), no more specific bound is needed.
What does this print, and why is List<Number> an acceptable argument for List<? super Integer>?
Prints [1, 2]. Number is a supertype of Integer, so List<Number> satisfies ? super Integer; the method can safely add Integer values into it because any supertype-of-Integer list can hold Integers.
Write a method copyAll(List<? super String> dest, List<? extends String> src) that copies every element from src into dest.
public static void copyAll(List<? super String> dest, List<? extends String> src) { for (String s: src) { dest.add(s); } } src is a producer (only read from) so it uses ? extends String; dest is a consumer (only written to) so it uses ? super String — a textbook application of the PECS rule, mirroring java.util.Collections.copy.
What does this print, and why is the commented-out list.add(4) line necessarily invalid if uncommented?
Prints 6.0. Reading elements as Number and summing them works because ? extends Number guarantees every element IS-A Number. Uncommenting list.add(4) would fail to compile because the compiler cannot verify the real underlying list's exact element type (could be List<Integer>, List<Long>, etc.) would safely accept an int/Integer being added through this producer-only view.
import java.util.*;
public static void addAllInts(List<? super Integer> dest) {
dest.add(1);
dest.add(2);
}
List<Number> numbers = new ArrayList<>();
addAllInts(numbers);
System.out.println(numbers);List<? extends Number> list = List.of(1, 2, 3);
double total = 0;
for (Number n : list) {
total += n.doubleValue();
}
// list.add(4);
System.out.println(total);