0Pricing
Java Academy · Lesson

Upper Bounded Wildcards

? extends T for producers.

Upper Bounded Wildcards is a free Java Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Generics Are Invariant

In Java, generics are invariant. A List<Integer> is not a List<Number>, even though Integer is a Number.

This surprises newcomers, but it keeps the type system sound. Wildcards relax this restriction safely.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> ints = List.of(1, 2, 3);
        // List<Number> nums = ints; // would NOT compile
        System.out.println("List<Integer> is not List<Number>");
        System.out.println(ints);
    }
}

The Upper Bounded Wildcard

? extends T is an upper bounded wildcard. It means "some unknown type that is T or a subtype of T".

List<? extends Number> can refer to a List<Integer>, a List<Double>, or a List<Number>.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> a = List.of(1, 2, 3);
        List<? extends Number> b = List.of(1.5, 2.5);
        System.out.println(a.get(0));
        System.out.println(b.get(0));
    }
}

Reading From a Producer

With ? extends Number you can safely read elements as Number.

Whatever the real element type, it is guaranteed to be at least a Number, so reading is type-safe.

import java.util.List;

public class Main {
    static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number n : list) total += n.doubleValue();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(sum(List.of(1, 2, 3)));
        System.out.println(sum(List.of(1.5, 2.5)));
    }
}

You Cannot Write to It

Here is the catch: you cannot add elements to a ? extends Number list (except null).

The compiler does not know the exact element type. It could be a List<Integer>, so adding a Double would be unsafe.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // list.add(1);   // does NOT compile
        // list.add(1.5); // does NOT compile
        System.out.println("Cannot add to ? extends Number");
        System.out.println("size = " + list.size());
    }
}

Why Writing Is Forbidden

Imagine if it were allowed. You could pass a List<Integer> as List<? extends Number> and then add a Double.

Later code reading the list as List<Integer> would get a ClassCastException. The compiler prevents this by banning writes.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Conceptual: this is why add is blocked
        System.out.println("Writing a Double into a List<Integer> would corrupt it");
        System.out.println("so ? extends bans all adds");
    }
}

Producer Use Case

Use ? extends T when a method only produces (reads out) values for you.

A method that copies elements out of a source collection is a perfect example.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static List<Number> copyOut(List<? extends Number> source) {
        List<Number> dest = new ArrayList<>();
        for (Number n : source) dest.add(n);
        return dest;
    }
    public static void main(String[] args) {
        System.out.println(copyOut(List.of(1, 2, 3)));
    }
}

Bounded Type Parameters vs Wildcards

Do not confuse <T extends Number> (a bounded type parameter) with ? extends Number (a wildcard).

  • Type parameter: names the type for use across the method.
  • Wildcard: an anonymous unknown subtype, used when you do not need to name it.
import java.util.List;

public class Main {
    static <T extends Number> T first(List<T> list) { return list.get(0); }
    public static void main(String[] args) {
        Integer i = first(List.of(10, 20));
        System.out.println(i);
    }
}

Multiple Bounds

A type parameter can have multiple bounds with &, like <T extends Number & Comparable<T>>.

Wildcards support only a single upper bound, so multiple bounds need a named type parameter.

import java.util.List;

public class Main {
    static <T extends Number & Comparable<T>> T max(List<T> list) {
        T best = list.get(0);
        for (T t : list) if (t.compareTo(best) > 0) best = t;
        return best;
    }
    public static void main(String[] args) {
        System.out.println(max(List.of(3, 9, 1, 7)));
    }
}

Wildcards With Collections.max

The JDK uses upper bounds widely. For example, summing or scanning a read-only collection.

Here we find the largest of a list typed as a producer.

import java.util.List;

public class Main {
    static double largest(List<? extends Number> nums) {
        double max = Double.NEGATIVE_INFINITY;
        for (Number n : nums) max = Math.max(max, n.doubleValue());
        return max;
    }
    public static void main(String[] args) {
        System.out.println(largest(List.of(4, 2, 9, 1)));
        System.out.println(largest(List.of(0.5, 9.9, 3.3)));
    }
}

Null Is the Only Writable Value

The one thing you can add to a ? extends T collection is null, because null is assignable to any reference type.

This is rarely useful, but it explains why add is not completely forbidden, only practically so.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Number>();
        // Only null is assignable; demonstrated conceptually
        System.out.println("Only null could be added; we avoid that");
        System.out.println("size = " + list.size());
    }
}

Reading Out as Object Always Works

Because every type extends Object, you can always read elements of any wildcard list as Object.

With an upper bound of Number, you get the bonus of reading them as Number directly.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2.0, 3L);
        for (Object o : nums) System.out.println(o.getClass().getSimpleName());
    }
}

Quick Check

Test your understanding of upper bounds.

Recap

You learned upper bounded wildcards:

  • ? extends T means an unknown subtype of T.
  • You can read elements as T (a producer).
  • You cannot add non-null elements.
  • Generics are invariant, and wildcards relax that safely.

Next, the mirror image: lower bounded wildcards.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2, 3);
        System.out.println("Upper bound recap: read " + nums.get(0));
    }
}

Frequently asked questions

Is the “Upper Bounded Wildcards” lesson free?

Yes — the full text of “Upper Bounded Wildcards” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Upper Bounded Wildcards”?

? extends T for producers. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Upper Bounded Wildcards” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Upper Bounded Wildcards
  2. Lower Bounded Wildcards
  3. The PECS Principle
  4. Wildcards in APIs
← Back to Java Academy