Wildcards in Java Generics

Naveen AnamaneniUpdated · 5 min read

An Integer is a Number, so surely a List<Integer> is a List<Number>? Surprisingly, no, and Java has a good reason. If it were, you could pass a list of integers to a method expecting numbers, and that method could legally add a Double to it, corrupting the list. Wildcards (?) are how you write methods that accept a family of related types while telling the compiler exactly what you'll do with them: only read, or only write.

Why List<Integer> Isn't a List<Number>

Compiler output
Wild.java:5: error: incompatible types: ArrayList<Integer> cannot be converted to List<Number>
    static void inv() { List<Number> n = new ArrayList<Integer>(); }
                                         ^

If that line were allowed, n.add(2.5) would put a Double into a list that other code believes holds only Integers. So generic types are invariant: List<A> and List<B> are unrelated, even when A extends B.

? extends: read as the bound. ? super: write the bound. ?: anything, read as Object

The Three Wildcards

Wildcards.java▶ Try it
import java.util.ArrayList;
import java.util.List;
 
public class Wildcards {
    static void printAll(List<? extends Number> numbers) {
        double total = 0;
        for (Number n : numbers) {
            total += n.doubleValue();
        }
        System.out.println(numbers + " total " + total);
    }
 
    static void addMarks(List<? super Integer> target) {
        target.add(91);
        target.add(88);
    }
 
    static int count(List<?> anything) {
        return anything.size();
    }
 
    public static void main(String[] args) {
        printAll(List.of(1, 2, 3));
        printAll(List.of(2.5, 0.5));
 
        List<Number> numbers = new ArrayList<>();
        addMarks(numbers);
        List<Object> objects = new ArrayList<>(List.of("Navika"));
        addMarks(objects);
        System.out.println(numbers + " " + objects);
 
        System.out.println(count(List.of("a", "b")) + count(List.of(1.5)));
    }
}
Output
[1, 2, 3] total 6.0
[2.5, 0.5] total 3.0
[91, 88] [Navika, 91, 88]
3

How it works

  1. List<? extends Number> means "a list of some type that is a Number or a subtype". It accepts List<Integer> and List<Double>. You can read each element as a Number.
  2. List<? super Integer> means "a list of some type that is Integer or a supertype". It accepts List<Integer>, List<Number> and List<Object>. You can add Integers to it.
  3. List<?> means "a list of some unknown type". You can call methods like size() that don't depend on the element type, and read elements only as Object.

Why the Restrictions Exist

The compiler only knows the wildcard's limits, not the actual type, so it blocks anything that might be unsafe:

Compiler output
Wild.java:3: error: incompatible types: int cannot be converted to CAP#1
    static void addNumber(List<? extends Number> nums) { nums.add(5); }
                                                                  ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number

nums might really be a List<Double>, so adding the Integer 5 could break it. CAP#1 is the compiler's name for "the unknown actual type". The same logic means reading from List<? super Integer> can only give an Object: the list might be a List<Object> holding anything.

Common Mistakes

Declaring List<Number> when callers have List<Integer>. Use List<? extends Number> for read-only input.

Trying to add to a ? extends list. Use ? super if the method writes into the list.

Using List<Object> to mean "any list". List<Object> only accepts List<Object>; List<?> accepts any list.

Interview Corner

Why is List

not a subtype of List? Generic types are invariant. If List<Integer> were a List<Number>, code holding it as List<Number> could add a Double, breaking the guarantee that the list contains only integers. Wildcards express safe relationships instead: List<Integer> is a subtype of List<? extends Number>.

What is the difference between List

, List and List? `Listis a list of some unknown type; you can read elements only asObjectand can't add (except null).List<? extends T>is a list ofTor a subtype; you can read elements asTbut not add.List<? super T>is a list ofTor a supertype; you can addTvalues but read elements only asObject`.

Practice

  1. Easy: Write static double max(List<? extends Number> values) and call it with a List<Integer> and a List<Double>.
  2. Harder: Write static void fillWithZeros(List<? super Integer> list, int n) and call it with a List<Integer>, a List<Number> and a List<Object>.

Quick check: Wildcards

4 questions
1Is List<Integer> a subtype of List<Number>?
2Which accepts List and List?
3Which lets a method add Integers?
4What can you read from a List<?>?

Key Takeaways

Was this tutorial helpful?

Lesson 245 of 482