Note that there are some explanatory texts on larger screens.

plurals
  1. POMultiple wildcards on a generic methods makes Java compiler (and me!) very confused
    primarykey
    data
    text
    <p>Let's first consider a simple scenario (<a href="http://ideone.com/jXOh0" rel="noreferrer">see complete source on ideone.com</a>):</p> <pre><code>import java.util.*; public class TwoListsOfUnknowns { static void doNothing(List&lt;?&gt; list1, List&lt;?&gt; list2) { } public static void main(String[] args) { List&lt;String&gt; list1 = null; List&lt;Integer&gt; list2 = null; doNothing(list1, list2); // compiles fine! } } </code></pre> <p>The two wildcards are unrelated, which is why you can call <code>doNothing</code> with a <code>List&lt;String&gt;</code> and a <code>List&lt;Integer&gt;</code>. In other words, the two <code>?</code> can refer to entirely different types. Hence the following does not compile, which is to be expected (<a href="http://ideone.com/moqSj" rel="noreferrer">also on ideone.com</a>):</p> <pre><code>import java.util.*; public class TwoListsOfUnknowns2 { static void doSomethingIllegal(List&lt;?&gt; list1, List&lt;?&gt; list2) { list1.addAll(list2); // DOES NOT COMPILE!!! // The method addAll(Collection&lt;? extends capture#1-of ?&gt;) // in the type List&lt;capture#1-of ?&gt; is not applicable for // the arguments (List&lt;capture#2-of ?&gt;) } } </code></pre> <p>So far so good, but here's where things start to get very confusing (<a href="http://ideone.com/YIy6Q" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class LOLUnknowns1 { static void probablyIllegal(List&lt;List&lt;?&gt;&gt; lol, List&lt;?&gt; list) { lol.add(list); // this compiles!! how come??? } } </code></pre> <p>The above code compiles for me in Eclipse and on <code>sun-jdk-1.6.0.17</code> in ideone.com, but should it? Is it not possible that we have a <code>List&lt;List&lt;Integer&gt;&gt; lol</code> and a <code>List&lt;String&gt; list</code>, the analogous two unrelated wildcards situations from <code>TwoListsOfUnknowns</code>?</p> <p>In fact the following slight modification towards that direction does not compile, which is to be expected (<a href="http://ideone.com/aS8WP" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class LOLUnknowns2 { static void rightfullyIllegal( List&lt;List&lt;? extends Number&gt;&gt; lol, List&lt;?&gt; list) { lol.add(list); // DOES NOT COMPILE! As expected!!! // The method add(List&lt;? extends Number&gt;) in the type // List&lt;List&lt;? extends Number&gt;&gt; is not applicable for // the arguments (List&lt;capture#1-of ?&gt;) } } </code></pre> <p>So it looks like the compiler is doing its job, but then we get this (<a href="http://ideone.com/A8dPb" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class LOLUnknowns3 { static void probablyIllegalAgain( List&lt;List&lt;? extends Number&gt;&gt; lol, List&lt;? extends Number&gt; list) { lol.add(list); // compiles fine!!! how come??? } } </code></pre> <p>Again, we may have e.g. a <code>List&lt;List&lt;Integer&gt;&gt; lol</code> and a <code>List&lt;Float&gt; list</code>, so this shouldn't compile, right?</p> <p>In fact, let's go back to the simpler <code>LOLUnknowns1</code> (two unbounded wildcards) and try to see if we can in fact invoke <code>probablyIllegal</code> in any way. Let's try the "easy" case first and choose the same type for the two wildcards (<a href="http://ideone.com/PzN9N" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class LOLUnknowns1a { static void probablyIllegal(List&lt;List&lt;?&gt;&gt; lol, List&lt;?&gt; list) { lol.add(list); // this compiles!! how come??? } public static void main(String[] args) { List&lt;List&lt;String&gt;&gt; lol = null; List&lt;String&gt; list = null; probablyIllegal(lol, list); // DOES NOT COMPILE!! // The method probablyIllegal(List&lt;List&lt;?&gt;&gt;, List&lt;?&gt;) // in the type LOLUnknowns1a is not applicable for the // arguments (List&lt;List&lt;String&gt;&gt;, List&lt;String&gt;) } } </code></pre> <p>This makes no sense! Here we aren't even trying to use two different types, and it doesn't compile! Making it a <code>List&lt;List&lt;Integer&gt;&gt; lol</code> and <code>List&lt;String&gt; list</code> also gives a similar compilation error! In fact, from my experimentation, the only way that the code compiles is if the first argument is an explicit <code>null</code> type (<a href="http://ideone.com/OYVEY" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class LOLUnknowns1b { static void probablyIllegal(List&lt;List&lt;?&gt;&gt; lol, List&lt;?&gt; list) { lol.add(list); // this compiles!! how come??? } public static void main(String[] args) { List&lt;String&gt; list = null; probablyIllegal(null, list); // compiles fine! // throws NullPointerException at run-time } } </code></pre> <p>So the questions are, with regards to <code>LOLUnknowns1</code>, <code>LOLUnknowns1a</code> and <code>LOLUnknowns1b</code>:</p> <ul> <li>What types of arguments does <code>probablyIllegal</code> accept?</li> <li>Should <code>lol.add(list);</code> compile at all? Is it typesafe?</li> <li>Is this a compiler bug or am I misunderstanding the capture conversion rules for wildcards?</li> </ul> <hr> <h3>Appendix A: Double LOL?</h3> <p>In case anyone is curious, this compiles fine (<a href="http://ideone.com/U65yx" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class DoubleLOL { static void omg2xLOL(List&lt;List&lt;?&gt;&gt; lol1, List&lt;List&lt;?&gt;&gt; lol2) { // compiles just fine!!! lol1.addAll(lol2); lol2.addAll(lol1); } } </code></pre> <hr> <h3>Appendix B: Nested wildcards -- what do they really mean???</h3> <p>Further investigation indicates that perhaps multiple wildcards has nothing to do with the problem, but rather a <em>nested</em> wildcard is the source of the confusion.</p> <pre><code>import java.util.*; public class IntoTheWild { public static void main(String[] args) { List&lt;?&gt; list = new ArrayList&lt;String&gt;(); // compiles fine! List&lt;List&lt;?&gt;&gt; lol = new ArrayList&lt;List&lt;String&gt;&gt;(); // DOES NOT COMPILE!!! // Type mismatch: cannot convert from // ArrayList&lt;List&lt;String&gt;&gt; to List&lt;List&lt;?&gt;&gt; } } </code></pre> <p>So it looks perhaps a <code>List&lt;List&lt;String&gt;&gt;</code> is not a <code>List&lt;List&lt;?&gt;&gt;</code>. In fact, while any <code>List&lt;E&gt;</code> is a <code>List&lt;?&gt;</code>, it doesn't look like any <code>List&lt;List&lt;E&gt;&gt;</code> is a <code>List&lt;List&lt;?&gt;&gt;</code> (<a href="http://ideone.com/XLoGE" rel="noreferrer">as seen on ideone.com</a>):</p> <pre><code>import java.util.*; public class IntoTheWild2 { static &lt;E&gt; List&lt;?&gt; makeItWild(List&lt;E&gt; list) { return list; // compiles fine! } static &lt;E&gt; List&lt;List&lt;?&gt;&gt; makeItWildLOL(List&lt;List&lt;E&gt;&gt; lol) { return lol; // DOES NOT COMPILE!!! // Type mismatch: cannot convert from // List&lt;List&lt;E&gt;&gt; to List&lt;List&lt;?&gt;&gt; } } </code></pre> <p>A new question arises, then: just what is a <code>List&lt;List&lt;?&gt;&gt;</code>?</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload