Java’s type parameter’s and wildcard’s (lack of) equivalence

Yet another post based on an interesting question I have encountered on StackOverflow quite a while ago.

But let us start with a slightly different thing. Once upon a time there was a question: what is the difference between ? extends Sometype and T extends Sometype? For example: <? extends Number> and <T extends Number> as type parameters of a method. The answers are correct, but they can be a bit misleading, especially when indicating, that in some cases they are equivalent.

Coming back to the original question, the confusion arises, when we want to run the following code:

static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

static void function(List<? extends Number> outer) {
    doesntCompile(new HashMap<Integer, List<Integer>>());
    compiles(new HashMap<Integer, List<Integer>>());

Which fails with the following error: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
        doesntCompile(new HashMap<Integer, List<Integer>>());

As it is always with this kind of questions, the answer can be found in the JLS. The doesntCompile method is easy to explain. In JLS (§4.5.1) we read, that:

A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):

  • ? extends T <= ? extends S if T <: S
  • ? extends T <= ?
  • ? super T <= ? super S if S <: T
  • ? super T <= ?
  • ? super T <= ? extends Object
  • <= T
  • <= ? extends T
  • <= ? super T

This means that ? extends Number indeed contains Integer and even, keeping in mind the variance of the List, List<? extends Number> contains List<Integer>. But that is not the case for Map<Integer, List<? extends Number>> and Map<Integer, List<Integer>>. However, after taking a closer look at the list above, we can see, that Map<Integer, ? extends List<? extends Number>> contains Map<Integer, List<Integer>>.

What about the compiles method? We will find the answer if we take a look at another paragraph of the JLS, namely §8.1.2:

A generic class declaration defines a set of parameterized types (§4.5), one for each possible parameterization of the type parameter section by type arguments. All of these parameterized types share the same class at run time.

The type parameter in the method signature, T, is matched against the input type, hence it is effectively assigned Integer. Unfortunately, we need to remember, that generics in Java is just a compile-time feature.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with
Get started