Ben Flynn
Ben Flynn

Reputation: 18922

Java collection with generics giving me non-applicable arguments error

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.

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> 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.

Upvotes: 5

Views: 1068

Answers (3)

Robin
Robin

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

Tom Anderson
Tom Anderson

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

Bohemian
Bohemian

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

Related Questions