The Java collection with generics gives me the argument error not applicable


I have a class FourByFourBoard that extends GameBoard. I define the following fields:

private Map<Integer,List<Key<? extends GameBoard>>> mGameTypeToKeysMap = new Hashtable<Integer,List<Key<? extends GameBoard>>>();

private List<Key<FourByFourBoard>> mFourByFourBoardKeys = new ArrayList<Key<FourByFourBoard>>();

In my constructor, I try to call:

mGameTypeToKeysMap.put(Game.FOUR_BY_FOUR, mFourByFourBoardKeys);

But I get this:

The method put(Integer, List<Key<? extends GameBoard>>) in the type Map<Integer,List<Key<? extends GameBoard>>> is not applicable for the arguments (int, List<Key<FourByFourBoard>>)

I can use a different approach to do what I'm trying to do, but after staring at the code for a little while I can't quite figure out why this doesn't work.


This problem might be simpler than I thought:

If I try:

Key<GameBoard> a = mFourByFourBoardKeys.get(0);

I get:

Type mismatch: cannot convert from Key<FourByFourBoard> to Key<GameBoard>

Even though:

GameBoard someBoard = new FourByFourBoard();

Is legal. So this is still a generics question, but the collections part was not important. And my head is still spinning a little.

A List<A extends B> and a List<B> are not the same, since to the first list you can only add A instances, and to the second you can add both A and B instances.

This is all perfectly well explained in the Java generics tutorial, more specifically on page 4 (Section Generics and Subtyping)


A small example illustrating this and matching your code a bit more closely

Map<Integer, List<List<? extends Number>>> a = new Hashtable<Integer,List<List<? extends Number>>>();
List<List<Double>> b = new ArrayList<List<Double>>();
a.put(0, b);//won't compile
List<List<? extends Number>> c = new ArrayList<List<? extends Number>>(  );
a.put( 1, c );//works perfectly

The reason why this not compiles is explained in that PDF to which I linked, and to quote the relevant part

Let’s test our understanding of generics. Is the following code snippet legal?

List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2

Line 1 is certainly legal. The trickier part of the question is line 2. This boils down to the question: is a List of String a List of Object. Most people’s instinct is to answer: “sure!”. Well, take a look at the next few lines:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: attempts to assign an Object to a String

I suggest to go through that whole PDF, and take a look at the (other) examples in that document.