Is there a way to make the following implementation in a type safe manner?
public void myMethod( Map<String, ? extends List<String>> map )
{
map.put("foo", Collections.singletonList("bar");
}
The above implementation doesn't work. It requires a Map<String, ? super List<String>>
to compile the method map.put()
correctly. But myMethod won't accept any subtype of List this way. So, I have to use Map<String, ? extends List<String>>
instead. How can I solve this problem in a type safe manner?
public void myMethod( Map<String, List<String>> map ) {
map.put("foo", Collections.singletonList("bar") );
}
You can't put a List
(the return type of Collections.singletonList()
into a Map
of ? extends List
since the actual type could be any implementation of List
. For example, it is not safe to put a generic List
into a Map<String,LinkedList>
since the List
might not be a LinkedList
. However, we can easily put a LinkedList
into a Map
of <String,List>
.
I think you were over thinking your generics. You do not have to use ? extends
for a non-generic class. For example, List<Object>
will hold any Object
, the ? extends
is not needed to add an object. A List<List<Foo>>
will only take List<Foo>
objects and not List<FooSubclass>
objects [Generic classes don't inherit based on their parameters]. This is where ? extends
comes into play. To add both List<Foo>
and List<FooSubclass>
to a List
, the type must be List<List<? extends Foo>>
.
Wouldn't Map<String, List<String>
work? Is there some particular reason you have to have a wildcard there at all?
There is a very simple principle when using Collections and Generics together. It is called "The Get and The Put Principle":
Use an "extends" wildcard when you only GET values out of a collection, use "super" wildcard when you only PUT values into a collection and don't use any wildcard when you want both get and put.
So, as you can see, the initial example is invalid, according to this principle.
Imagine if someone passed you a Map<String, ArrayList<String>>
. The value you're putting in is the result of Collections.singletonList which is not an ArrayList
.
You cannot accept any subtype of List in the Map and then expect to be able to add your own, possibly incompatible, subtype.