As we know, generic types in Java are invariant. However, this completely strips all polymorphic behaviour away from generic types.
Sometimes we just want to take in any List that is a subtype of Number. How do we do that?

Well, this is where wildcards come in!
Java lets you enable covariance and even contravariance for generic types with the use of wildcards!!!

Covariance using Upper Bound Wildcards

class main {
	public static void main(String[] args) {
		sumOverList(
			new ArrayList<Integer>(Arrays.asList(1,2,3))
		);
		sumOverList(
			new ArrayList<Float>(Arrays.asList(1f,2f,3f))
		);
		sumOverList(
			new ArrayList<Double>(Arrays.asList(1d,2d,3d))
		);
	}
	
	static double sumOverList(List<? extends Number> list) {
		double sum = 0;
		for (Number x: list) {
			sum += x.doubleValue();
		}
		return sum;
	}
}

Here we see that the use of the wildcard syntax ? together with extends we have allowed covariance of generic types that are subsets of the Number type!

Covariance using Unbounded Wildcards

You can also use unbounded wildcards like List<?>
This is the equivalent of using covariance with List<? extends Object>

Contravaraince using Lower abound Wildcards

import java.util.*;
abstract class Animal{
	protected int numberOfLimbs;
	public Animal(int n) {
		numberOfLimbs = n;
	}
	
	public static void main(String[] args) {
		// These all work because Dog either extends or implement
		// these classes/interface.
		// Dog is a subtype of these types
		// and the method accepts contravariant types of Dog
		insertDogIntoList(new ArrayList<Object>());
		insertDogIntoList(new ArrayList<Animal>());
		insertDogIntoList(new ArrayList<CanBark>());
		insertDogIntoList(new ArrayList<Dog>());
		// This fails as PitBull is a subtype of Dog.
		// Covariant types are not allowed.
		insertDogIntoList(new ArrayList<PitBull>());
	}
 
	// Takes in any List that can hold a Dog! and adds a Dog!
	static void insertDogIntoList(List<? super Dog> x) {
		x.add(new Dog()); // Dawgggg!
	}
}
 
interface CanBark {}
 
class Dog extends Animal implements CanBark {
	public Dog() {
		super(4);
	}
}
 
class PitBull extends Dog {
	public PitBull() {
		super();
	}
}

Compiler Error!

argument mismatch; ArrayList<PitBull> cannot be converted to List<? super Dog>

What’s Next?

java-generics-type-erasure