I defined a generic function in java with the signature
<V> List<V> sortedValuesFromMap(Map<?, Collection<V>> keysValues, Comparator<V> comp)
which takes a Map mapping any type of keys to a Collection of some defined type V, and a comparator of type V. The method works great and the Java compiler does not complain about type incompatibility.
But now when I want to apply this method to a map of the type Map<String, Set<String>> and the AlphanumComparator (see here) the compiler says :
The method sortedValuesFromMap(Map<?,Collection<V>>, Comparator<V>)
in the type MyUtils is not applicable for the arguments
(Map<String,Set<String>, AlphanumComparator)
Turning Collection to Set in the signature of sortedValuesFromMap would fix it – but I do not want to do that. So why is Java forcing me to do so, although Set<E> is implementing Collection<E>?
PS: If someone is interested in my code:
public static <V> List<V> sortedValuesFromMap(Map<?, Collection<V>> keysValues,
Comparator<V> comp) {
List<V> values = new LinkedList<V>();
for (Collection<V> col : keysValues.values()) {
values.addAll(col);
}
Collections.sort(values, comp);
return values;
}
Just as a List<Dog> is not a List<Animal>, a Map<String, Set<String>> is not a Map<String, Collection<String>> and it's not a Map<?, Collection<String>>.
The solution here is to add a wildcard in place of Set to allow a subclass in the generic type parameter.
// Add v
public static <V> List<V> sortedValuesFromMap(Map<?, ? extends Collection<V>> keysValues,
Comparator<V> comp) {
Your problem is that Map<String,Set<V>> is not a subtype of Map<String,Collection<V>>. Think about what type x can be, if it's legal to write x.put("Hello",new ArrayList<V>());
In this case, x could be a Map<String,Collection<V>>, since an ArrayList<V> is certainly a Collection<V>. But it couldn't be a Map<String,Set<V>>, since an ArrayList<V> is not a Set<V>. Therefore it's untrue to say that any Map<String,Set<V>> "is a" Map<String,Collection<V>>.
The type that you want is either Map<String,? extends Collection<V>> or Map<?,? extends Collection<V>>. Both Map<String,Set<V>> and Map<String,Collection<V>> are subtypes of these types.
Related
How come one must use the generic type Map<?, ? extends List<?>> instead of a simpler Map<?, List<?>> for the following test() method?
public static void main(String[] args) {
Map<Integer, List<String>> mappy =
new HashMap<Integer, List<String>>();
test(mappy);
}
public static void test(Map<?, ? extends List<?>> m) {}
// Doesn't compile
// public static void test(Map<?, List<?>> m) {}
Noting that the following works, and that the three methods have the same erased type anyways.
public static <E> void test(Map<?, List<E>> m) {}
Fundamentally, List<List<?>> and List<? extends List<?>> have distinct type arguments.
It's actually the case that one is a subtype of the other, but first let's learn more about what they mean individually.
Understanding semantic differences
Generally speaking, the wildcard ? represents some "missing information". It means "there was a type argument here once, but we don't know what it is anymore". And because we don't know what it is, restrictions are imposed on how we can use anything that refers to that particular type argument.
For the moment, let's simplify the example by using List instead of Map.
A List<List<?>> holds any kind of List with any type argument. So i.e.:
List<List<?>> theAnyList = new ArrayList<List<?>>();
// we can do this
theAnyList.add( new ArrayList<String>() );
theAnyList.add( new LinkedList<Integer>() );
List<?> typeInfoLost = theAnyList.get(0);
// but we are prevented from doing this
typeInfoLost.add( new Integer(1) );
We can put any List in theAnyList, but by doing so we have lost knowledge of their elements.
When we use ? extends, the List holds some specific subtype of List, but we don't know what it is anymore. So i.e.:
List<? extends List<Float>> theNotSureList =
new ArrayList<ArrayList<Float>>();
// we can still use its elements
// because we know they store Float
List<Float> aFloatList = theNotSureList.get(0);
aFloatList.add( new Float(1.0f) );
// but we are prevented from doing this
theNotSureList.add( new LinkedList<Float>() );
It's no longer safe to add anything to the theNotSureList, because we don't know the actual type of its elements. (Was it originally a List<LinkedList<Float>>? Or a List<Vector<Float>>? We don't know.)
We can put these together and have a List<? extends List<?>>. We don't know what type of List it has in it anymore, and we don't know the element type of those Lists either. So i.e.:
List<? extends List<?>> theReallyNotSureList;
// these are fine
theReallyNotSureList = theAnyList;
theReallyNotSureList = theNotSureList;
// but we are prevented from doing this
theReallyNotSureList.add( new Vector<Float>() );
// as well as this
theReallyNotSureList.get(0).add( "a String" );
We've lost information both about theReallyNotSureList, as well as the element type of the Lists inside it.
(But you may note that we can assign any kind of List holding Lists to it...)
So to break it down:
// ┌ applies to the "outer" List
// ▼
List<? extends List<?>>
// ▲
// └ applies to the "inner" List
The Map works the same way, it just has more type parameters:
// ┌ Map K argument
// │ ┌ Map V argument
// ▼ ▼
Map<?, ? extends List<?>>
// ▲
// └ List E argument
Why ? extends is necessary
You may know that "concrete" generic types have invariance, that is, List<Dog> is not a subtype of List<Animal> even if class Dog extends Animal. Instead, the wildcard is how we have covariance, that is, List<Dog> is a subtype of List<? extends Animal>.
// Dog is a subtype of Animal
class Animal {}
class Dog extends Animal {}
// List<Dog> is a subtype of List<? extends Animal>
List<? extends Animal> a = new ArrayList<Dog>();
// all parameterized Lists are subtypes of List<?>
List<?> b = a;
So applying these ideas to a nested List:
List<String> is a subtype of List<?> but List<List<String>> is not a subtype of List<List<?>>. As shown before, this prevents us from compromising type safety by adding wrong elements to the List.
List<List<String>> is a subtype of List<? extends List<?>>, because the bounded wildcard allows covariance. That is, ? extends allows the fact that List<String> is a subtype of List<?> to be considered.
List<? extends List<?>> is in fact a shared supertype:
List<? extends List<?>>
╱ ╲
List<List<?>> List<List<String>>
In review
Map<Integer, List<String>> accepts only List<String> as a value.
Map<?, List<?>> accepts any List as a value.
Map<Integer, List<String>> and Map<?, List<?>> are distinct types which have separate semantics.
One cannot be converted to the other, to prevent us from doing modifications in an unsafe way.
Map<?, ? extends List<?>> is a shared supertype which imposes safe restrictions:
Map<?, ? extends List<?>>
╱ ╲
Map<?, List<?>> Map<Integer, List<String>>
How the generic method works
By using a type parameter on the method, we can assert that List has some concrete type.
static <E> void test(Map<?, List<E>> m) {}
This particular declaration requires that all Lists in the Map have the same element type. We don't know what that type actually is, but we can use it in an abstract manner. This allows us to perform "blind" operations.
For example, this kind of declaration might be useful for some kind of accumulation:
static <E> List<E> test(Map<?, List<E>> m) {
List<E> result = new ArrayList<E>();
for(List<E> value : m.values()) {
result.addAll(value);
}
return result;
}
We can't call put on m because we don't know what its key type is anymore. However, we can manipulate its values because we understand they are all List with the same element type.
Just for kicks
Another option which the question does not discuss is to have both a bounded wildcard and a generic type for the List:
static <E> void test(Map<?, ? extends List<E>> m) {}
We would be able to call it with something like a Map<Integer, ArrayList<String>>. This is the most permissive declaration, if we only cared about the type of E.
We can also use bounds to nest type parameters:
static <K, E, L extends List<E>> void(Map<K, L> m) {
for(K key : m.keySet()) {
L list = m.get(key);
for(E element : list) {
// ...
}
}
}
This is both permissive about what we can pass to it, as well as permissive about how we can manipulate m and everything in it.
See also
"Java Generics: What is PECS?" for the difference between ? extends and ? super.
JLS 4.10.2. Subtyping among Class and Interface Types and JLS 4.5.1. Type Arguments of Parameterized Types for entry points to the technical details of this answer.
This is because the subclassing rules for generics are slightly different from what you may expect. In particular if you have:
class A{}
class B extends A{}
then
List<B> is not a subclass of List<A>
It's explained in details here and the usage of the wildcard (the "?" character) is explained here.
This question already has an answer here:
Bounded-wildcard related compiler error
(1 answer)
Closed 8 years ago.
Following on from this question, which provides a solution but doesn't explain it (unfortunately, the links in the answers are now dead):
Take the following method:
void method(Map<?, ?> myMap) {
Set<Map.Entry<?, ?>> set = myMap.entrySet();
...
}
Simple, no? However, this fails to compile on jdk1.7.0_25:
incompatible types
required: java.util.Set<java.util.Map.Entry<?,?>>
found: java.util.Set<java.util.Map.Entry<capture#1 of ?,capture#2 of ?>>
WTF? Map.entrySet() is specified as returning an object of type Set<Map.Entry<K, V>>, so in the example above, myMap.entrySet() returns a Set<Map.Entry<?, ?>>. But it doesn't compile!
Even weirder, from the linked question at the top, changing the method to this makes it compile:
void method(Map<?, ?> myMap) {
Set<? extends Map.Entry<?, ?>> set = myMap.entrySet();
...
}
WTF??? Calling entrySet on a Map<?, ?> returns a Set<Map.Entry<K, V>>, which can't be assigned to a variable of type Set<Map.Entry<K, V>>, but it can to a variable of type Set<? extends Map.Entry<K, V>>?????
Can anyone shed light on what's going on here? And does this mean that, whenever I write a method using a wildcard type at least 2 levels deep, I have to remember to make it ? extends ... somewhere?
Each of those ? can vary independently, so there's no guarantee that the <?,?> in the declaration of myMap matches the <?,?> in the declaration of set.
What this means is that once I have a Set<Map<?,?>>, I can put any type of Map into that set, because Map<?,?> is a supertype of all types of Map. But this is not a property that Set<Map<String,Integer>> (for example) has - it's far more restrictive in terms of what types of map I can put into it. So Set<Map<?,?>> is not a supertype of Set<Map<String,Integer>>. But myMap.entrySet() could easily be a Set<Map<String,Integer>>, depending on what myMap is. So the compiler has to forbid us from assigning it to a variable of type Set<Map<?,?>>, and that's what's happening.
On the other hand, Set<? extends Map<?,?>> is a supertype of Set<Map<String,Integer>>, because Map<String,Integer> is a subtype of Map<?,?>. So it's OK to assign myMap.entrySet() to a variable of type Set<? extends Map<?,?>>.
Note that there's nothing special about String and Integer here, but myMap has to be a map of something!
You could write
<K, V> void method(Map<K, V> myMap) {
Set<Map.Entry<K, V>> set = myMap.entrySet();
...
I am wondering what is wrong with this code:
Map <? extends String, ? extends Integer> m = null;
Set<Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
The compiler complains with the error message:
Type mismatch: cannot convert from Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>> to Set<Map.Entry<? extends String,? extends Integer>>
What should the type of s be? Eclipse suggests Set<?> but I am trying to get more specific than that.
This issue is addressed in this old Apache thread:
The problem is that the entrySet() method is returning a
Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>>,
which is incompatible with the type Set<Map.Entry<? extends K, ? extends V>>.
It's easier to describe why if I drop the extends K and extends V part.
So we have Set<Map.Entry<?, ?> and Set<Map.Entry<capture-of ?, capture-of ?>>.
The first one, Set<Map.Entry<?, ?>> is a set of Map.Entries of different
types - ie it is a heterogeneous collection. It could contain a
Map.Entry<Long, Date> and a Map.Entry<String, ResultSet>> and any other
pair of types, all in the same set.
On the other hand, Set<Map.Entry<capture-of ?, capture-of ?>> is a homogenous
collection of the same (albeit unknown) pair of types. Eg it might be a
Set<Map.Entry<Long, Date>>, so all of the entries in the set MUST be
Map.Entry<Long, Date>.
The crux of the problem is that top-level wildcards capture, meaning they are essentially one-off type parameters. In contrast, nested wildcards don't capture, and have somewhat of a different meaning.
So, removing the bounds for simplicity, declaring
Map<?, ?> m;
means "a map of some specific unknown type of keys and some specific unknown type of values".
But declaring
Set<Map.Entry<?, ?>> s;
means "a set of entries of any type of key and value".
So that's where you run into trouble because the expression m.entrySet() doesn't want to return that but instead "a set of entries of some specific unknown type of keys and some specific unknown type of values". And those types are incompatible because generics aren't covariant: A Set<Type> isn't a Set<SuperType>.
(See this fascinating post, which helps tease apart the nuances of nested wildcards: Multiple wildcards on a generic methods makes Java compiler (and me!) very confused.)
One workaround is to use a capture helper method, which takes advantage of the fact that formal type parameters can be nested:
private <K extends String, V extends Integer> void help(final Map<K, V> map) {
final Set<Map.Entry<K, V>> entries = map.entrySet();
// logic
}
...
Map<? extends String, ? extends Integer> m = null;
help(m);
That's a contrived example since String and Integer are both final, but it shows the concept.
A simpler workaround is the following:
Set<? extends Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
This means adding non-null elements to s isn't allowed, but in the case of the Set returned by entrySet, the add and addAll methods are unsupported anyway (thanks to newacct for clarifying this point).
I have a question regarding generics:
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
with super it's possible to instantiate a HashMap<Object,Object> for a <? super String>.
However then you can add only objects which extends String ( in this case only String itself).
Why don't they forbid by compilation error as well as happens with the extends wildcard.
I mean if once created a Map <Object, Object> it's possible only to add Strings.. why not forcing to create a Map<String, String> in the first place? (like it happens with the extends wildcard)
Again I know the difference between super and extends concerning generics. I would like just to know the details I have aboved-mentioned.
Thanks in advance.
Let's use List instead of Map for brevity.
Essentially, practical meaning of extends and super can be defined as follows:
List<? extends T> means "a List you can get T from"
List<? super T> means "a List you can put T into"
Now you can see that there is nothing special about extends - behavior of extends and super is completely symmetric:
List<? extends Object> a = new ArrayList<String>(); // Valid, you can get an Object from List<String>
List<? extends String> b = new ArrayList<Object>(); // Invalid, there is no guarantee that List<Object> contains only Strings
List<? super String> a = new ArrayList<Object>(); // Valid, you can put a String into List<Object>
List<? super Object> b = new ArrayList<String>(); // Invalid, you cannot put arbitrary Object into List<String>
I think you are thrown off because you picked a collection type. Collections are rarely used as consumers and thus a lower bound (? super X) is not put on their element types. A more appropriate example is predicate.
Consider a method such as <E> List<E> filter(List<? extends E> p, Predicate<? super E> p). It will take a list l and a predicate p and return a new list containing all elements of l which satisfy p.
You could pass in a List<Integer> and a Predicate<Number> which is satisfied by all multiples of 2.5. The Predicate<Number> would become a Predicate<? super Integer>. If it did not, you could not invoke filter as follows.
List<Integer> x = filter(Arrays.asList(1,5,8,10), Predicates.multipleOf(2.5));
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
Since Java Generics are based on type erasure, with this line you didn't create a MashMap<Object,Object>. You just created an instance of the HashMap class; the type parameters get lost immediately after this line of code and all that stays is the type of your mappa1 variable, which doesn't even mention Object. The type of the new expression is assignment-compatible with the type of mappa1 so the compiler allows the assignment.
In general, the type parameters used with new are irrelevant and to address this issue, Java 7 has introduced the diamond operator <>. All that really matters is the type of mappa1, which is is Map<? super String, ? super String>; as far as the rest of your code is concerned, this is the type of the instantiated map.
The problem you're describing doesn't exist.
It is because your reference is declared as Map<? super String, ? super String>. But your actual object can hold any object since it's HashMap<Object,Object>
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
map1.put("", "");
//you can put only string to map1
//but you can do this
Map map2 = map1;
map2.put(23, 234);
the same can be described by a better example:
String a = "a".
a.length(); // legal.
Object b = a;
b.length() // compilation error
I ran into a bug in my code where I was using the wrong key to fetch something from a Java map that I believed was strongly typed using Java generics. When looking at the Map Javadocs, many of the methods, including get and remove, take an Object as the parameter instead of type K (for a Map defined as Map). Why is this? Is there a good reason or is it an API design flaw?
I think this is for backwards compatibility with older versions of the Map interface. It's unfortunate that this is the case however as you're right, it would be much better if this took the correct type.
Because the map will return a value if the object passed to the get method is equal to any key stored in the map. Equal does not mean that they have to be of the same type, but that the key's and the passed object's equal methods are implemented in such a way, that the different object types are mutually recognized as equal.
The same applies of course to the remove method.
Example of valid code, which would break (not compile) if the get method only allowed parameters of type K:
LinkedList<Number> k1 = new LinkedList<Number>();
k1.add(10);
ArrayList<Integer> k2 = new ArrayList<Integer>();
k2.add(10);
Map<LinkedList<Number>, String> map = new HashMap<LinkedList<Number>, String>();
map.put(k1, "foo");
System.out.println(map.get(k2));
This was done so that if the type parameter is a wildcard, these methods can still be called.
If you have a Map<?, ?>, Java won't allow you to call any methods that are declared with the generic types as arguments. This prevents you from violating the type constraints so you cannot, for instance, call put(key, value) with the wrong types.
If get() were defined as get(K key) instead of the current get(Object key), it too would have been excluded due to this same rule. This would make a wildcarded Map practically unusable.
In theory, the same applies to remove(), as removing an object can never violate the type constraints either.
Here is an example of code that would not have been possible if get had been declared as get(T key):
public static <K,V> Map<K, V> intersect(Map<? extends K, ? extends V> m1, Map<? extends K, ? extends V> m2) {
Map<K,V> result = new HashMap<K, V>();
for (Map.Entry<? extends K, ? extends V> e1 : m1.entrySet()) {
V value = m2.get(e1.getKey()); // this would not work in case of Map.get(K key)
if (e1.getValue().equals(value)) {
result.put(e1.getKey(), e1.getValue());
}
}
return result;
}
e1.getKey() returns an object of some unknown subtype of K (the subtype used by m1), but m2 uses a potentially different subtype of K. Had Map.get() been declared as get(K key), this usage would not have been allowed.