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 type x,
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.
List{?}, Map{String, ? extends Number}
The meaning of {?} is to declare a union type
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
It's easy to see that, List<Integer> is a subtype of List{? extends Number}; and List{? extends Number} is a subtype of List{? extends Object}
However, there's no way that Foo{?} is a subtype of a Foo<x>.
In our syntax, <> is reserved for substituting type vars with types. So we write List<String>, C2<Integer>, etc. It's easy to understand their meaning - just replace T's with String in the source code of List, we get a good-old plain class.
interface List<String>
String get(int)
This cannot be done for wildcard - it makes no sense
interface List<?>
? get(int)
So it is not allowed to new ArrayList{?}(), or class 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 of List<x> for some unknown type x. This is wildcard capture
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
Even though the exact type of x is unknown at compile time, we can still do the substitution
interface List<x>
x get(int)
so we can make sense of the call obj.get(0); it returns x, and x is a subtype of Object; so we can assign the return value to an Object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With