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 withString
in 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
x
is unknown at compile time, we can still do the substitutionso we can make sense of the call
obj.get(0)
; it returnsx
, andx
is a subtype ofObject
; so we can assign the return value to anObject
.