Search

Categories

Links

Referers

Why is the Capture of a Wildcard Incompatible with Itself?

Jun 10 2004, 05:59:17 PM PDT »Java»Language Comments [17]
Sometimes you get an error like this:
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.

Post a Comment:
Comments are closed for this entry.
Comments:

This is really ugly syntax, and totally non-intuitive. I find that in order to get the compiler to stop emitting warnings, I have to keep extending variable type declarations, until they stretch across the screen. Once the warning suppression annotation is working, I think I'll just get into the habit of putting that at every class definition, and avoid generics. Long and bizarre declarations are worse than simple doc comments. In this particular example, I don't understand what "? extends Entry<String,?>" means. Let's see, "This is an iterator of things that are any instances of Map.Entry which map Strings to anything". Is this just some magic syntax to disable the wildcard capture inference? If I create APIs which make extensive use of generics, they will be much harder to use than before. The best feature of the Java language has now been eliminated. It is no longer easy or fun to use. Class cast exceptions are actually relatively rare compared to null pointer exceptions. Adding support to the language to detect NPEs at compile time is much easier to implement and understand, and it would eliminate the leading runtime exception. Unfortunately, such a language feature would severely break backwards compatibility. P.S. Your blog needs to support new paragraphs or at least have a help link explaining how to add them. I don't see new paragraphs when I preview my comment.

Posted by Brian S O'Neill on June 11, 2004 at 08:17 AM PDT #

If you do not like generics, use -source 1.4. If you do want to use generics, normally you are not required to use wildcards. Wildcards are there to extend the expressive power of the Java type system and most of the time you should not need them (unless you design APIs).

Posted by Peter Ah� on June 11, 2004 at 08:04 PM PDT #

I do not know how to enable HTML syntax in comments nor how to create new paragraphs in them. But I'll keep looking.

Posted by Peter Ah&eacute; on June 11, 2004 at 08:07 PM PDT #

If you have some examples where generics gets in you way, please let me have a look. Post a link or send me the sources: (Peter.Ahe at sun com)

Posted by Peter Ah&eacute; on June 11, 2004 at 08:09 PM PDT #

Why does " MyIterator<Entry<String,?>>mit = (MyIterator<Entry<String,?>>)it; " work? I thought List<String> was not a subtype of Collection<String>, so why is MyIterator<...> a subtype of Iterator<...>?

Posted by Keith Lea on July 15, 2004 at 09:25 PM PDT #

I do not under the difference between the wildcard and the capture of the wildcard. You say that the wildcard ? stands for all possible types. That's not how I understand wildcards. That would mean that a List<?> is a list of elements of all possible types, while in fact it is a list of one particular (unknown) type. The wildcard stands for one representative from a family of types, not for all types. And that's exactly what the capture seems to be. So, what's the difference? Are they treated differently regarding type convertibility or anything? What's the point in having both? Can you expand on that?

Posted by Angelika Langer on August 02, 2004 at 10:16 PM PDT #

BTW, the tool eats the angle brackets. That's pretty bad when you want to talk about generics. ;-)

How do I say "List of unbounded wildcard" without using angle brackets?

In the comment above it should read: That would mean that a "List of unbounded wildcard" is a list of elements of all possible types, while in fact it is a list of one particular (unknown) type.

Posted by Angelika Langer on August 02, 2004 at 10:22 PM PDT #

Keith,

List<Foo> is a subtype of Collection<Foo>, List<String> is not a subtype of List<Object>

Posted by Peter Ah� on August 04, 2004 at 11:18 PM PDT #

<div>Angelika,</div> <div>I think you're pretty close in your understanding of wildcards. It is not true that a List<?> is a list of all possible types. It means a List of some unknown type. The problem is what can we assume about the unknown type, consider:</div>
class Test {
    List<?> l;
    void m() {
	l.add(l.remove(0));
    }
}
<div>It would apparently be safe to allow this to compile. We just removed an element from a list and now we put it back. But what happens if a different thread has changed the field l between the call to l.remove and l.add. Then if the unknown type of the object referenced by l at the time l.remove is called could be List<Integer> meaning that we just removed an Integer from that list. Now the other thread gets to execute, so l could point to an entirely different List, e.g. List<String>. Then we call l.add with an Integer on a List<String>.</div> <div>In this example we actually captured the List<?> twice, first when we call l.remove, next when we call l.add (technically, the capture happens exactly when we "dot into" l). I just argued that the unknown types captured at those those points could be compeletely unrelated types. That is what I mean when I say "all possible types". A variable declared to be a List<?> can be a List of any possible type. Maybe I should have said "any", not "all". Please forgive me, English is my second language.</div> <div>So can you harness wildcard capture conversion to do something similar to the method m above? Yes, write a generic method:</div>
class Test {
    List<?> l;
    void m() {
	m2(l);
    }
    <T> static void m2(List<T> l2) {
	l2.add(l2.remove(0));
    }
}
<div>Here we only capture the type of l once, namely when we call m2 from m. Notice that for the duration of the call to m2, we have a name for the unknown, captured type: T.</div>

Posted by Peter Ah� on August 05, 2004 at 12:20 AM PDT #

how this just [unchecked] unchecked conversion
----------------------
public class Main {
// void printCollection(Collection c) {
Iterator i = c.iterator();
// for (int k = 0; k < c.size(); k++) {
// System.out.println(i.next());
// }
// }
void printCollection(Collection<Object> c) {
for (Object e : c) {
System.out.println(e);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
ArrayList<Object> al = new ArrayList();// question is here
al.add("a");
new Main().printCollection(al);
}
}
--------------

ArrayList<String> al = new ArrayList();
is error I can catch on

Posted by pprun on September 27, 2004 at 12:34 AM PDT #

Answer to pprun:

Your question is a bit unclear, but I guess that you wonder what is wrong with this line:

ArrayList<Object> = new ArrayList();
The problem is that you left out the type parameters on the second line:
ArrayList<Object> = new ArrayList<Object>();

Posted by Peter Ah� on September 27, 2004 at 12:21 PM PDT #

Hi, I have an issue involving captured wildcards. I have a static method which in turns calls a static 'factory' method:
public static <T> Predicate<T> andPredicate(Predicate<? super T> predicate1, Predicate<? super T> predicate2) {
    return AndPredicate.getInstance(predicate1, predicate2);
}
And this method calls (in AndPredicate):
public static <T> Predicate<T> getInstance(Predicate<? super T> predicate1, Predicate<? super T> predicate2) {
    if (predicate1 == null || predicate2 == null) {
        throw new IllegalArgumentException("Predicate must not be null");
    }
    return new AndPredicate<T>(predicate1, predicate2);
}
The constructor that it calls looks like:
public AndPredicate(Predicate<? super T> predicate1, Predicate<? super T> predicate2) {
    super();
    iPredicate1 = predicate1;
    iPredicate2 = predicate2;
}
This does not compile, giving the error:
Error:  line (264) <T>getInstance(org.apache.commons.collections.Predicate<? super T>,org.apache.commons.collections.Predicate<? super T>) in
org.apache.commons.collections.functors.AndPredicate cannot be applied to
(org.apache.commons.collections.Predicate<capture of ? super T>,org.apache.commons.collections.Predicate<capture of ? super T>)
How should I resolve this? I can't think of a way of retooling it so that I can still ensure that the two predicates can act on objects of type T or objects of T's super-class.

Thanks in advance,

John Watkinson

Posted by John Watkinson on May 05, 2005 at 03:04 PM PDT #

John, Super bounded wildcards mess up inference. So you need to tell the compiler what to infer:
return AndPredicate.<T>getInstance(predicate1, predicate2);

Posted by Peter Ahe on May 05, 2005 at 07:16 PM PDT #

<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta http-equiv=Content-Type content="text/html; charset=windows-1252"> <meta name=ProgId content=Word.Document> <meta name=Generator content="Microsoft Word 9"> <meta name=Originator content="Microsoft Word 9"> <link rel=File-List href="./Question%20about%20generics_archivos/filelist.xml"> <title>A question about generics</title> <!--[if gte mso 9]><xml> <o:DocumentProperties> <o:Author>Matthijs Snel</o:Author> <o:Template>Normal</o:Template> <o:LastAuthor>.</o:LastAuthor> <o:Revision>2</o:Revision> <o:TotalTime>53</o:TotalTime> <o:Created>2005-05-14T14:11:00Z</o:Created> <o:LastSaved>2005-05-14T14:11:00Z</o:LastSaved> <o:Pages>1</o:Pages> <o:Words>188</o:Words> <o:Characters>1072</o:Characters> <o:Company>Accenture</o:Company> <o:Lines>8</o:Lines> <o:Paragraphs>2</o:Paragraphs> <o:CharactersWithSpaces>1258</o:CharactersWithSpaces> <o:Version>9.2812</o:Version> </o:DocumentProperties> </xml><![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:ActiveWritingStyle Lang="EN-US" VendorID="64" DLLVersion="131078" NLCheck="1">1</w:ActiveWritingStyle> <w:PunctuationKerning/> </w:WordDocument> </xml><![endif]--> <style> <!-- /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:EN-US; mso-fareast-language:EN-US;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} --> </style> </head> <body lang=ES style='tab-interval:36.0pt'> <div class=Section1> <p class=MsoNormal><span lang=EN-US>Allright, if I post without previewing it works fine. Here´s the original text again, apologies. I’ve got a class Group which is a type of collection. The group contains a static method that extracts all groups contained in a given collection and returns a SortedSet that contains these groups. The method has the following signature:</span>

<p class=MsoNormal><span lang=EN-US><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>public static <E extends Comparable<? super E>> SortedSet<? extends Group<E>> createGroups( Collection<? extends E> col )<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US>Now, if I try to do the following, for example:</span>

<p class=MsoNormal><span lang=EN-US><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>List<Integer> a = new ArrayList<Integer>();<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'><span style='mso-tab-count:3'>                </span><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>a.add(new Integer(1)); a.add(new Integer(1)); a.add(new Integer(2));<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'><span style='mso-tab-count:3'>                </span><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>SortedSet<Group<Integer>> ss = Group.createGroups(a);<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US>The compiler starts complaining:</span>

<p class=MsoNormal><span lang=EN-US><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>incompatible types<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>found<span style="mso-spacerun: yes">   </span>: java.util.SortedSet<capture of ? extends Group<java.lang.Integer>><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>required: java.util.SortedSet<Group<java.lang.Integer>><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'><span style="mso-spacerun: yes">                        </span>SortedSet<Group<Integer>> ss = Group.createGroups(a);<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'>Why does this happen? Shouldn’t a </span><span lang=EN-US style='font-size:11.0pt;font-family: "Courier New"'>SortedSet<Group<E>></span><span lang=EN-US style='font-size:11.0pt'> be compatible with a </span><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>SortedSet<? extends Group<E>></span><span lang=EN-US style='font-size:11.0pt'>? <o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'>The code compiles when I change the static method to return a </span><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>SortedSet<Group<E>></span><span lang=EN-US style='font-size:11.0pt'> (which I don’t want), or declare the SortedSet as </span><span lang=EN-US style='font-size:11.0pt;font-family:"Courier New"'>SortedSet<? extends Group<Integer>>. </span><span lang=EN-US style='font-size: 11.0pt'>However, I don’t understand why it doesn’t work as is.<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'>Your help is much appreciated.<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'><![if !supportEmptyParas]>&nbsp;<![endif]><o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'>Cheers<o:p></o:p></span>

<p class=MsoNormal><span lang=EN-US style='font-size:11.0pt'>Matthijs<o:p></o:p></span>

</div> </body> </html>

Posted by Matthijs Snel on May 14, 2005 at 12:18 PM PDT #

Matthijs,

Try this code:
SortedSet<? extends Group<Integer>> ss = Group.createGroups(a);

Posted by Peter von der Ahé on May 16, 2005 at 03:04 PM PDT #

I have tried to create a minimal example that shows the two questions I have in regards to this. <p /> Please see inlined questions in comments. Apart from that, is my solution to the problems the most correct/most elegant one? Or how should these issues be resolved? <p />
-------------------------------------------------------

// public interface Parent { // This does not work. Why?
public interface Parent<D extends Child> { // This works. 
  List<Child> getChildren();
}

-------------------------------------------------------

public interface Child {
}

-------------------------------------------------------

public class ParentImpl1 implements Parent {
  private List<ChildImpl1> children;
  
  public List<ChildImpl1> getChildren() {
    return children;
  }
}

-------------------------------------------------------


public class ParentImpl1 implements Parent {
  private List<ChildImpl1> children;
  
  // In our case, this must return implementation and not interface 
  public List<ChildImpl1> getChildren() { 
    return children;
  }
}

-------------------------------------------------------

public class ChildImpl1 {
}

-------------------------------------------------------

public class Test {
  public static void loopChildren(Parent p) {
    // This works
    final List<Child> children = p.getChildren();
    for(Child child : children) {
    }

    // This does not work. Why?
    /*
    for(Child child : p.getChildren()) {
    }
    */
  }
}

-------------------------------------------------------

Posted by Mattias Jiderhamn on October 06, 2005 at 04:07 AM PDT #

Mattias, the blog software doesn't handle generic types very well. Please try reposting your question on the generics forum: http://forum.java.sun.com/forum.jspa?forumID=316

Posted by Peter von der Ah&eacute; on October 06, 2005 at 04:19 AM PDT #

Java is a trademark of Sun Microsystems, Inc.
Copyright © 2006,2007 Peter von der Ahé