javaunbounded-wildcard

After capture conversion, why I got two fresh type-variables with the generic type only having one type parameter?


A simple class is defined:

class SimpleClass<T> {
  public T t1;
  public T t2;

  public SimpleClass(T t1, T t2) {
    this.t1 = t1;
    this.t2 = t2;
  }
}

test code:

SimpleClass<?> p = new SimpleClass<String>("a", "b");
p.t1 = p.t2;

Compile this code, I got the error message:

error: incompatible types: CAP#1 cannot be converted to CAP#2
    p.t1 = p.t2;
            ^
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends Object from capture of ?
    CAP#2 extends Object from capture of ?

The declard type of p is SimpleClass<?>, after capture conversion, I thought I got some class like this:

class SimpleClass_cap#1<CAP#1> {
  public CAP#1 t1;
  public CAP#1 t2;

  public SimpleClass(CAP#1 t1, CAP#1 t2) {
    this.t1 = t1;
    this.t2 = t2;
  }
}

which is based on jls:

Let G name a generic type declaration (§8.1.2, §9.1.2) with n type parameters A_1,...,A_n with corresponding bounds U_1,...,U_n.

There exists a capture conversion from a parameterized type G<T_1,...,T_n> (§4.5) to a parameterized type G<S_1,...,S_n>, where, for 1 ≤ i ≤ n :

• If T_i is a wildcard type argument (§4.5.1) of the form ?, then S_i is a fresh type variable whose upper bound is U_i[A_1:=S_1,...,A_n:=S_n] and whose lower bound is the null type (§4.1).

My question is: After delcared p as SimpleClass<?>, why I got two fresh type-variables CAP#1 and CAP#2?


Solution

  • Capture conversion happens twice in the line p.t1 = p.t2;. Each of the two ps are converted, so each of their types have a fresh type variable. The type of the first p is SimpleClass<CAP1>, and the type of the second p is SimpleClass<CAP2>.

    This is specified in 6.5.6, where it discusses expression names. Let's try to find out the type of p.t1. This is of the form Q.Id, so we go to 6.5.6.2, where this case applies

    If Q is an expression name, let T be the type of the expression Q:

    So we need to find T, which is the type of p in our case. Let's go to 6.5.6.1 where it talks about simple expression names.

    If the expression name appears in an assignment context, invocation context, or casting context, then the type of the expression name is the declared type of the field, local variable, or parameter after capture conversion.

    This is where p is converted to SimpleClass<CAP1>.

    Then if you go back to 6.5.6.2 and follow the rest of the section, we can conclude that p.t1 is of type CAP1. And the same thing applies to the right hand side of the assignment.