MyClass.java:9: incompatible types
found : java.util.Iterator<java.util.Map.Entry<java.lang.String,capture of ?>>
required: java.util.Iterator<java.util.Map.Entry<java.lang.String,?>>
Iterator<Entry<String,?>> it = map.entrySet().iterator();
^
In short: Iterator<Entry<String,capture of ?>> is not compatible with Iterator<Entry<String,?>>. The relevant lines of my program are:
Map<String,?> map;
Iterator<Entry<String,?>> it = map.entrySet().iterator();
Is "capture of ?" not a subtype of "?"? Yes, but that is not what we need here. We need the type arguments to be of the same type (you should already know that a List<String> is not a subtype of List<Object>).
This seems silly, isn't a wildcard the same type as it self? Yes, but here we have a wildcard and a type variable ("capture of ?" actually means that a wildcard has been converted into a type variable using capture conversion).
Then why bother with this capture conversion, my program is obviously correct and capture conversion gets in my way? No, your program is wrong. Capture conversion can actually help you understand why.
How? First of all you must learn how to read a wildcard aloud, e.g. read "Map<String,?>" as a Map from String to an unknown type. The second argument to Map is unknown but at some point the compiler needs to reason about a specific type, e.g., when you write "map.entrySet()". The compiler must reason about the specific type of the object "map" refers to at this point at runtime. The wildcard must not longer be wild (unknown) so the compiler captures a snapshot of "?" in an anonymous type variable at that point in the program, i.e., "capture of ?". So the difference between "?" and "capture of ?" is that the former refers to all possible types and the latter refers to a particular type at a particular point in you program.
I still do not see the point of all this? Ok, lets extend the above program a little bit, first we need a special (but perfectly sensible iterator):
class MyIterator<E> implements Iterator<E> {
Set<E> iteratedSet;
// other stuff left out for brevity
}
Now do this:
Entry<String,Number> unrelatedEntry; ... MyIterator<Entry<String,?>>mit = (MyIterator<Entry<String,?>>)it mit.iteratedSet.add(unrelatedEntry);
Now all of a sudden you have saved an Integer somewhere it might not belong!!! This will almost certainly lead to a class cast exception later in your program (e.g, if map referred to an instance of HashMap<String,String>).
OK, now I understand my problem. How do I solve it? That is simple: just write:
Map<String,?> map;
Iterator<? extends Entry<String,?>> it = map.entrySet().iterator();
Notice the added "? extends".
UPDATE: I have disabled comments. Please visit the
generics forum
instead.
My intention of this blog is to post answers to frequently asked questions about the new features added to the Java Programming Language in Tiger.