Reputation: 18922
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 typeMap<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.
EDIT
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>
toKey<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.
Upvotes: 5
Views: 1068
Reputation: 36601
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)
Edit
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.
Upvotes: 3
Reputation: 47163
?
means 'some definite type which i don't know'; ? extends Foo
means 'some definite type which i don't know, but which extends Foo'.
FourByFourBoard
is a definite type which extends GameBoard
, but the compiler cannot know that it is the same such definite type as the one mentioned in the definition of mGameTypeToKeysMap.
To illustrate, i think it would be allowed to write:
mGameTypeToKeysMap = new Hashtable<Integer,List<Key<EightByEightBoard>>>();
At which point your attempted put
would be very wrong.
Upvotes: 1
Reputation: 424983
The problem is that the generic types must match exactly. Try this:
private List<Key<? extends GameBoard>> mFourByFourBoardKeys = new ArrayList<Key<? extends GameBoard>>();
You can still add FourByFourBoard
instances to the list OK, but the map will accept the list now.
Note: I tested this using standard JDK classes with a similar pattern, so anyone can copy-paste this code for themselves:
Map<Integer, List<Comparable<? extends Number>>> map = new HashMap<Integer, List<Comparable<? extends Number>>>();
List<Comparable<? extends Number>> list = new ArrayList<Comparable<? extends Number>>();
list.add(new Long(0));
map.put(new Integer(1), list);
Upvotes: 1