Parameterization Well Formedness and Capture Conversion in Java

113 views Asked by At

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!

1

There are 1 answers

2
ZhongYu On BEST ANSWER

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.