Skip to main content

Wildcards in Generics

In generic code, the question mark (?), called the wildcard, represents an unknown type.

Wildcards are particularly useful as the type of a parameter in a method. They allow you to write incredibly flexible code that can operate on collections of different types without knowing the exact type in advance.


1. Unbounded Wildcards (?)​

The unbounded wildcard <?> stands for any type. It is useful when you are writing a method that can be implemented using functionality provided in the Object class, or when the code is independent of the type parameter (like List.size() or List.clear()).

Example​

Imagine you want to write a method that prints any type of List.

import java.util.Arrays;
import java.util.List;

public class LabWildcard1 {

// Method accepts a List of ANY type
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.print(elem + " ");
}
System.out.println();
}

public static void main(String args[]) {
List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> strList = Arrays.asList("A", "B", "C");

// Both calls are completely valid!
printList(intList);
printList(strList);
}
}

[!WARNING] While List<?> can accept any list, you cannot add any elements to a List<?> (except null). Because the compiler doesn't know what type the list actually holds, it prevents you from adding anything to ensure type safety.


2. Upper Bounded Wildcards (? extends T)​

You use an upper bounded wildcard to relax the restrictions on a variable. Suppose you want to write a method that works on List<Integer>, List<Double>, and List<Number>. You can achieve this by using an upper bounded wildcard: <? extends Number>.

This means the method accepts a list of Number or any subclass of Number.

Example​

import java.util.Arrays;
import java.util.List;

public class LabWildcard2 {

// Accepts a List of Number, or any subclass (Integer, Double, Float...)
public static double sum(List<? extends Number> list) {
double sum = 0.0;
for (Number n : list) {
sum += n.doubleValue(); // Safe because we know it's at least a Number
}
return sum;
}

public static void main(String args[]) {
List<Integer> ints = Arrays.asList(1, 2, 3);
System.out.println("Sum of ints: " + sum(ints));

List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
System.out.println("Sum of doubles: " + sum(doubles));

List<String> strings = Arrays.asList("1", "2");
// sum(strings); // COMPILE ERROR! String does not extend Number.
}
}

3. Lower Bounded Wildcards (? super T)​

The lower bounded wildcard restricts the unknown type to be a specific type or a super class of that type. It is expressed using the super keyword: <? super T>.

This is most often used when you want a method to be able to safely insert elements into a collection.

Example​

Suppose you want to write a method that adds Integer objects to a list. To maximize flexibility, you want the method to work on List<Integer>, List<Number>, and List<Object> (anything that can hold an Integer).

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

public class LabWildcard3 {

// Accepts a List of Integer, or any superclass of Integer (Number, Object)
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i); // Safe! Because a List of Number/Object can definitely hold Integers.
}
System.out.println("List after addition: " + list);
}

public static void main(String args[]) {
List<Integer> intList = new ArrayList<>();
addNumbers(intList);

List<Number> numList = new ArrayList<>();
addNumbers(numList);

List<Object> objList = new ArrayList<>();
addNumbers(objList);

List<Double> doubleList = new ArrayList<>();
// addNumbers(doubleList); // COMPILE ERROR! Double is NOT a superclass of Integer.
}
}