I\'ve been beating my head against this one for awhile and thought that maybe some fresh eyes will see the issue; thanks for your time.
import java.util.*;
clas
This happens because of the way capture conversion works:
There exists a capture conversion from a parameterized type
G
to a parameterized type1,...,Tn> G
, where, for 1 ≤ i ≤ n :1,...,Sn>
- If
Ti
is a wildcard type argument of the form? extends Bi
, thenSi
is a fresh type variable [...].Capture conversion is not applied recursively.
Note the end bit. So, what this means is that, given a type like this:
Map, List>>
// │ │ └ no capture (not applied recursively)
// │ └ T2 is not a wildcard
// └ T1 is a wildcard
Only "outside" wildcards are captured. The Map
key wildcard is captured, but the List
element wildcard is not. This is why, for example, we can add to a List
, but not a >
List>
. The placement of the wildcard is what matters.
Carrying this over to TbinList
, if we have an ArrayList
, the wildcard is in a place where it does not get captured, but if we have a TbinList>
, the wildcard is in a place where it gets captured.
As I alluded to in the comments, one very interesting test is this:
ArrayList> test3 = new TbinList<>();
We get this error:
error: incompatible types: cannot infer type arguments for TbinList<>
ArrayList> test3 = new TbinList<>();
^
reason: no instance(s) of type variable(s) T exist so that
TbinList conforms to ArrayList>
So there's no way to make it work as-is. One of the class declarations needs to be changed.
Additionally, think about it this way.
Suppose we had:
class Derived1 extends Base {}
class Derived2 extends Base {}
And since a wildcard allows subtyping, we can do this:
TbinList extends Base> test4 = new TbinList();
Should we be able to add a Tbin
to test4
? No, this would be heap pollution. We might end up with Derived2
s floating around in a TbinList
.