skip to content

Why does `Comparator<? super T>` appear in signatures like `Collections.sort` and `Stream.sorted`, and what flexibility does it give callers?

level: seniorimportance: should knowfreq 40%

answer

  1. Comparator only reads/consumes T → Consumer Super
  2. Comparator<Number> can sort List<Integer>
  3. ? super admits comparators of T and its parents
  4. comparing(): super on input, extends on key
  5. List.sort / Stream.sorted use `? super`

basics

~20 s

A comparator that knows how to compare T's supertype can also compare T's, since every T is-a supertype. Accepting Comparator<? super T> lets you reuse one general comparator (e.g. for Animal) to sort a list of a subtype (e.g. Dog).

solid answer

~40 s

A `Comparator<? super T>` is a comparator that consumes T values — it only ever *reads in* objects to compare them, never produces T. By PECS (Consumer Super), the API declares the parameter as `Comparator<? super T>` rather than `Comparator<T>`. The payoff is reuse: a `Comparator<Number>` can sort a `List<Integer>`, because anything that can compare two Numbers can certainly compare two Integers (each Integer is-a Number). Without the wildcard, you'd be forced to write or wrap a `Comparator<Integer>` even when a perfectly good general-purpose comparator already exists. You see this in `Collections.sort(List<T>, Comparator<? super T>)`, `List.sort`, `Stream.sorted`, `Comparable`-based `min`/`max`, and `Comparator.comparing`. It is the lower-bounded wildcard applied to a functional consumer interface.

go deeper

for a junior

Recognizes that Comparator<? super T> lets a more general comparator be used.

for a middle

Explains that a comparator consumes T and can use a supertype comparator on a subtype list.

for a senior

Ties it to PECS/contravariance, cites JDK sort signatures, and reasons about which comparators are admitted.

for a principal

Discusses API design trade-offs of variance on functional interfaces and explains nested PECS in comparing/thenComparing.

## A comparator is a consumer A `Comparator<X>` has one core method, `int compare(X a, X b)`. It takes two X values **in** and returns an int — it never returns an X. In PECS terms, a comparator **consumes** the type it compares. By the rule **Consumer Super**, an API that needs to compare T values should accept `Comparator<? super T>`, not the stricter `Comparator<T>`. ## Why the wildcard buys reuse Suppose you have a general comparator: ```java Comparator<Number> byDouble = Comparator.comparingDouble(Number::doubleValue); ``` Now you want to sort a `List<Integer>`. Every Integer **is-a** Number, so `byDouble` can compare any two Integers perfectly well. With a lower-bounded signature this just works: ```java static <T> void sort(List<T> list, Comparator<? super T> c) { ... } List<Integer> ints = new ArrayList<>(List.of(3, 1, 2)); sort(ints, byDouble); // T = Integer, Comparator<Number> satisfies <? super Integer> ``` If the parameter were `Comparator<Integer>`, the compiler would **reject** `byDouble` (a `Comparator<Number>`) and force you to create a redundant Integer-specific comparator. The wildcard avoids that duplication. ## Why a *subtype* comparator would be unsafe Could you pass a `Comparator<Integer>` to sort a `List<Number>`? No — and the `? super` bound correctly forbids it. A `Comparator<Integer>` cannot compare arbitrary Numbers (it might be handed a Double). `? super T` admits comparators of T and its **supertypes** only, which is exactly the safe set. ## Where it shows up in the JDK ```java Collections.sort(List<T> list, Comparator<? super T> c) List<E>.sort(Comparator<? super E> c) Stream<T>.sorted(Comparator<? super T> c) Stream<T>.min(Comparator<? super T> c) Stream<T>.max(Comparator<? super T> c) Comparator.comparing(Function<? super T, ? extends U> keyExtractor) ``` Note `comparing` is doubly variant: the key extractor *consumes* T (`Function<? super T, …>`) and *produces* U (`…, ? extends U>`) — Producer Extends on the output, Consumer Super on the input. That single line is PECS applied twice. ## Connecting back to the leaf This is the lower-bounded wildcard (`<? super T>`) used on a **functional consumer interface** rather than a collection. The same contravariant reasoning applies: a consumer of a more general type is also a valid consumer of any of its subtypes, so accepting `? super T` maximizes the set of usable comparators while staying type-safe.

  • Can a `Comparator<Object>` be used to sort a `List<String>`? Why?
    Yes. Object is a supertype of String, so `Comparator<Object>` satisfies `Comparator<? super String>`; an object comparator (e.g. by toString) can compare any two Strings.
  • In `Comparator.comparing(Function<? super T, ? extends U>)`, why is the function's input `? super T` and its output `? extends U`?
    The function consumes T to extract a key, so its input is Consumer-Super; it produces the comparison key U, so its output is Producer-Extends. It is PECS applied to both ends of the function.

saying these in an interview costs you the question

  • Thinking a `Comparator<Integer>` can be used to sort a `List<Number>` (it can't).
  • Saying `Comparator<T>` is preferable for flexibility (the wildcard is more flexible).
  • Treating a comparator as a producer of T (it consumes).
  • Forgetting that `comparing` applies PECS twice (super on input, extends on key).

context