Given the following two class definitions:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
And the following type declaration:
C1<C2<?>> a;
Intuitively it feels the declared type a should be valid, but this is not the way JDK-8u45 behaves. Instead we get something like the following output:
Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
C1<C2<?>> a;
^
where T is a type-variable:
T extends C1<T> declared in class C1
1 error
(Edit: I was being a dingus here, this part has been answered: C2<?> does not extend C1<C2<?>>. The issue regarding the declaration of c below is still an open question, though.)
But C2<?> does extend C1<C2<?>>, which would appear to trivially satisfy the bound. Examination of the JLS provides no further illumination so far as I can see. It really should just be as simple as satisfying the bound by the subtype relation, since C2<?> is not a wildcard type and therefore capture conversion is just an identity conversion on the argument.
There are situations where it becomes a little less clear, for example, take the following class definitions:
class C3<T extends C3<?>> {}
class C4<Y, Z> extends C3<C4<Z, Y>> {}
class C5<X extends C3<X>> {
void accept(X x);
}
All of this is fine, but then if we try the following declaration:
C5<C6<?, ?>> b;
Things become stranger. C6<?, ?> is a subtype of C3<C6<?, ?>>, so the declaration should be valid according to my interpretation of the specification as given above regarding the declaration C1<C2<?>>. The problem is that clearly not every possible subtype of C6<?, ?> actually satisfies that bound, so now for example C5.accept() resolves its parameter type to C6<?, ?> and so can accept arguments which violate the bounding on X, i.e. any where the parameterizations of Y and Z are not identical.
Where am I going wrong here? Is my understanding of the subtype relationship insufficient?
(Edit: The following part of the question is still unanswered, but I've moved it to a new question here since it's a completely different issue really... Sorry for making a mess and not using the site very well haha...)
Aside from this, I'm also having some problems with capture conversion in similar situations. Take the following type declaration:
C1<? extends C2<?>> c;
Unlike the similar declaration a at the start, this compiles fine in JDK-8u45. If we examine the specification for capture conversion, though, it appears this declaration should result in a compile time error this time.
In particular, the upper bound of the new type variable capture CAP#T is given by glb(Bi, Ui[A1:=S1,...,An:=Sn]), where in this case Bi resolves to the wildcard bound C2<?> and Ui[A1:=S1,...,An:=Sn] resolves to C1<CAP#T>.
From this, glb(C2<?>, C1<CAP#T>) resolves to the intersection type C2<?> & C1<CAP#T>, which is invalid, because C2<?> and C1<CAP#T> are both class types, not interface types, but neither one of them is a subtype of the other.
This (apparent) rule violation is made more clear in the definition of the intersection type itself.
I'm sure it's not a bug and I'm just making some simple mistakes somewhere... but if nobody here can shed any light on this for me I'll try the compiler-dev mailing list or something.
Thanks for any help!
While
C2<x> extends C1<C2<x>>for any reference typex,it is not the case that
C2<?> extends C1<C2<?>>A wildcard
?is not a type. It is a type argument. The syntax though is very deceiving (by design).Let's use a different syntax - if there's any 1st-level wildcard, use
{}instead of<>, e.g.The meaning of
{?}is to declare a union typeIt's easy to see that,
List<Integer>is a subtype ofList{? extends Number}; andList{? extends Number}is a subtype ofList{? extends Object}However, there's no way that
Foo{?}is a subtype of aFoo<x>.In our syntax,
<>is reserved for substituting type vars with types. So we writeList<String>, C2<Integer>, etc. It's easy to understand their meaning - just replaceT's withStringin the source code ofList, we get a good-old plain class.This cannot be done for wildcard - it makes no sense
So it is not allowed to
new ArrayList{?}(), orclass MyList implements List{?}So, how can we use
List{?}? What methods we can call on it?When the type of an expression is a
List{?}, we know that it is an object, and the object must belong to a subclass ofList<x>for some unknown typex. This is wildcard captureEven though the exact type of
xis unknown at compile time, we can still do the substitutionso we can make sense of the call
obj.get(0); it returnsx, andxis a subtype ofObject; so we can assign the return value to anObject.