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