Explain Codes LogoExplain Codes Logo

What does the question mark in Java generics' type parameter mean?

java
promises
best-practices
collections
Alex KataevbyAlex Kataev·Sep 25, 2024
TLDR

The ? in Java generics is a wildcard symbol that means "unknown type". It's used liberally when you don’t need to strictly define type parameter bounds. For more controlled scenarios, ? extends T is employed where T or its descendant types are desired. You get read-access to them but can't add new ones. On the other hand, ? super T allows you to insert instances of T or its ancestors into a collection. This liberates your code from rigid type check and fosters greater flexibility. Here's a handy code sample reflecting bounded wildcard usage:

public void sum(List<? extends Number> numbers) { double total = 0.0; for(Number n : numbers) { total += n.doubleValue(); // So summing up numbers feels this easy, huh? } }

Tuple of Wildcards: Commonly Misunderstood Concepts

Swashbuckling with ? extends T

The wildcard ? extends T is your friend when you are dealing with classes that are subtypes/twig of T. A Rand McNally direction when you want to access methods defined in the superclass T or subclasses thereof. Applying this wildcard unlocks a flexible gateway welcoming various types. Who wouldn't enjoy such open-mindedness, amirite? 😏

public void printFirst(List<? extends HasWord> list) { if (!list.isEmpty()) { HasWord first = list.get(0); System.out.println(first); //Will the real HasWord please stand up! } } List<Sentence> sentences = ...; printFirst(sentences); // Look ma! Sentence inherits from HasWord

Parties with ? super T

? super T is the life of the party when you want to insert instances of T or its subclasses into a collection. It's a champion of the PECS (Producer Extends, Consumer Super) acronym, ensuring your method can deliver gifts to collections expecting T or a more general type. Gift-giving was never this generous and unrestricted since Santa's been on a diet 🎅:

public void fillWithDefaultWords(List<? super Sentence> list) { list.add(new Sentence("default")); //Everyone gets a Sentence, like an Oprah show! } List<HasWord> words = ...; fillWithDefaultWords(words); // HasWord is a superclass of Sentence, so we're cool!

Bounded Wildcards: Jack of all Trades

Using ? extends T and ? super T broadcasts intentions crystal clear than raw types or unbounded wildcards. It's a return ticket to better design and safer code. For instance, sorting algorithms can inhale a Comparator<? super T>:

Collections.sort(list, new Comparator<SomeClass>() { public int compare(SomeClass o1, SomeClass o2) { // comparison logic, better than a Harry Potter sorting hat! } });

It's clear the comparator will choose a dance partner from SomeClass or its superclasses.

Life Lessons from Type Safety

  • Use ? extends T to keep things read-safe, no adding randoms to the party.
  • Prefer ? super T to insert elements while maintaining the original type's dignity.
  • With bounded wildcards, you can ditch the instanceof checks; polymorphism is the new black.

A Day in the Life of Generics

Imagine an API builder for different data types. Bounded wildcards shape up versatile methods:

public <E> void processData(Collection<? extends E> data) { for (E element : data) { // process element as you please } }

Your Collection is now a hotbed for any subtype of E. Winning!