Skip to main content

Generics puzzle

Posted by carcassi on August 4, 2011 at 9:18 AM PDT

You know that generics are too hard when you can't even figure out why something does not compile.

Found this (simplified example of something that I have encountered):

 interface Test1<T> {
public List<T> getList();
}

public void test1(Test1<?> arg) {
List<?> result = arg.getList();
}

interface Test2<T> {
public List<Collection<T>> getList();
}

public <T> void test2Typed(Test2<T> arg) {
List<Collection<T>> result = arg.getList();
}

public void test2Captured(Test2<?> arg) {
List<Collection<?>> result = arg.getList();
}

test2Captured() does not compile:

org/epics/pvmanager/expression/GenericTest.java:[33,48] incompatible types
found : java.util.List<java.util.Collection<capture#878 of ?>>
required: java.util.List<java.util.Collection<?>>

Now, I am not casting anything. I am just assigning to the same type. Yes, it's a different capture, but I don't see what I could do that I couldn't do with a direct call to the argument... And why would test1() compile? I don't see the difference...

What am I missing?

 

Related Topics >>

Comments

<p>See the code below:</p> <pre> // No compile error. Can ...

See the code below:

// No compile error. Can add HashSet&lt;Integer&gt; to a List&lt;Collection&lt;?&gt;&gt;
public void test2Dummy() {
List&lt;Collection&lt;?&gt;&gt; result = new ArrayList&lt;Collection&lt;?&gt;&gt;();
result.add(new HashSet&lt;Integer&gt;());
}

// Compile error. Cannot add HashSet&lt;Integer&gt; to List&lt;Collection&lt;T&gt;&gt; as Integer does not derive from T
public &lt;T&gt; void test2Typed(Test2&lt;T&gt; arg) {
List&lt;Collection&lt;T&gt;&gt; result = arg.getList();
result.add(new HashSet&lt;Integer&gt;());
}

// Compile error. Cannot cast arg.getList() to List&lt;Collection&lt;?&gt;&gt; as you could then add HashSet&lt;Integer&gt;
public void test2Captured(Test2&lt;?&gt; arg) {
List&lt;Collection&lt;?&gt;&gt; result = arg.getList();
result.add(new HashSet&lt;Integer&gt;());
}

The ? character is a wildcard, and any two ? within a method don't have to mean the same thing. Looking at the "test2Captured" method, the compiler is treating the ? used in Test2<?> as different from the ? used in "List<Collection<?>>", and so you cannot cast arg.getList() to List<Collection<?>>.

Seems pretty odd, but I think the code above demonstrates it well enough.

<p>Thanks!!! Yes, now I see it. Figured was something like ...

Thanks!!! Yes, now I see it. Figured was something like that, but I couldn't find the right permutation...

I guess this implies "argument type" not same meaning of "declared code"... I'll have to ponder more on that...

 

<p>Hehe yeah, I was scratching my head over that this ...

Hehe yeah, I was scratching my head over that this afternoon. I never noticed that you can create an ArrayList<Collection<?>> and store it in a matching variable type, but you cannot cast an ArrayList<Collection<T>> to that same type.

Here's some more example code that I created to test this out:

<span class="Apple-tab-span" style="white-space:pre"> </span>/*
<span class="Apple-tab-span" style="white-space:pre"> </span> * You can create a list to hold Integers, and then assign it to a list
<span class="Apple-tab-span" style="white-space:pre"> </span> * of &quot;unknown things&quot;.
<span class="Apple-tab-span" style="white-space:pre"> </span> */
<span class="Apple-tab-span" style="white-space:pre"> </span>List&lt;?&gt; unknowns = new ArrayList&lt;Integer&gt;();<br />

<span class="Apple-tab-span" style="white-space:pre"> </span>/*
<span class="Apple-tab-span" style="white-space:pre"> </span> * You can create a list to hold &quot;collections holding Integers&quot;. This is
<span class="Apple-tab-span" style="white-space:pre"> </span> * perfectly fine and works as expected.
<span class="Apple-tab-span" style="white-space:pre"> </span> */
<span class="Apple-tab-span" style="white-space:pre"> </span>List&lt;Collection&lt;Integer&gt;&gt; collectionsOfIntegers = new ArrayList&lt;Collection&lt;Integer&gt;&gt;();<br />

<span class="Apple-tab-span" style="white-space:pre"> </span>/*
<span class="Apple-tab-span" style="white-space:pre"> </span> * You can create a list to hold &quot;collections holding unknown things&quot;.
<span class="Apple-tab-span" style="white-space:pre"> </span> * This is perfectly fine, and you can add any collection to this list.
<span class="Apple-tab-span" style="white-space:pre"> </span> * From the time it was constructed, we could guarantee that NOONE will
<span class="Apple-tab-span" style="white-space:pre"> </span> * assume it holds &quot;collections of Integers&quot; or similar.
<span class="Apple-tab-span" style="white-space:pre"> </span> */
<span class="Apple-tab-span" style="white-space:pre"> </span>List&lt;Collection&lt;?&gt;&gt; collectionsOfUnknowns = new ArrayList&lt;Collection&lt;?&gt;&gt;();<br />

<span class="Apple-tab-span" style="white-space:pre"> </span>/*
<span class="Apple-tab-span" style="white-space:pre"> </span> * You can create a list that holds &quot;collections holding Integers&quot;, but
<span class="Apple-tab-span" style="white-space:pre"> </span> * you CANNOT cast it to a list of &quot;collections holding unknown things&quot;.
<span class="Apple-tab-span" style="white-space:pre"> </span> * At the time it was constructed, it was meant to just hold
<span class="Apple-tab-span" style="white-space:pre"> </span> * &quot;collections of Integers&quot;. By casting it, you are saying it can now
<span class="Apple-tab-span" style="white-space:pre"> </span> * hold &quot;collections of unknown things&quot;, which just isn't true at all.
<span class="Apple-tab-span" style="white-space:pre"> </span> * It can only hold &quot;collections of Integers&quot;!
<span class="Apple-tab-span" style="white-space:pre"> </span> */
<span class="Apple-tab-span" style="white-space:pre"> </span>List&lt;Collection&lt;?&gt;&gt; collectionsOfBadness = new ArrayList&lt;Collection&lt;Integer&gt;&gt;();

<span class="Apple-tab-span" style="white-space:pre"> </span>/*
<span class="Apple-tab-span" style="white-space:pre"> </span> * You can create a list that holds &quot;collections holding Integers&quot;, and
<span class="Apple-tab-span" style="white-space:pre"> </span> * then copy it into a list of &quot;collections holding unknown things&quot;!
<span class="Apple-tab-span" style="white-space:pre"> </span> */
<span class="Apple-tab-span" style="white-space:pre"> </span>List&lt;Collection&lt;?&gt;&gt; collectionsOfGoodness = new ArrayList&lt;Collection&lt;?&gt;&gt;(collectionsOfIntegers);