Anonymous class creation and diamonds: bound 'E extends B<E>'

Georgiy Rakov georgiy.rakov at oracle.com
Wed Apr 22 16:37:50 UTC 2015


Hello,

let's consider following example:

    class B<V> {}

    class Foo<E extends B<E>> {
         public Foo<E> complexMethod(E a) { return this; }
    }

    public class Test65  {
         public static void check() {
             Foo t4 = new Foo<>() {
             };
         }
    }

This code successfully compiles on JDK9b60. However according to my 
understanding the compilation should have failed as per new spec. Could 
you please tell if this understanding is correct.

The reasons why I think that the compilation should have failed are 
presented below.

E is inferred as B<Y>, where Y is a fresh type variable with the upper 
bound B<Y>.  If this is correct the given code should cause compilation 
failure according to following new assertions presented in the 
JDK-8073593 issue comment 
<https://bugs.openjdk.java.net/browse/JDK-8073593?focusedCommentId=13622110&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13622110>:

    ***It is a compile-time error if the superclass or superinterface
    type of the anonymous class, T, or any subexpression of T, has one
    of the following forms:
    - A type variable (4.4) that was not declared as a type parameter
    (such as a type variable produced by capture conversion (5.1.10))
    - An intersection type (4.9)
    - A class or interface type, where the class or interface
    declaration is not accessible from the class or interface in which
    the expression appears.***
    The term "subexpression" includes type arguments of parameterized
    types (4.5), bounds of wildcards (4.5.1), and element types of array
    types (10.1). It excludes bounds of type variables.*** 

The reason for this is that anonymous class super type, which is 
parameterized, has a type argument (subexpression) being a /type 
variable which was not declared as a type parameter.//
///
The fact that E is inferred as a type variable with the upper bound 
becomes more obvious if we decide to override complexMethod:

         Foo t4 = new Foo<>() {
             public Foo<? extends B> complexMethod(B a){ return this; }
             //public Foo<B> complexMethod(B a){ return this; } ;//this causes compilation failure
         };

In this case providing return type as Foo<B> causes compilation failure. 
Only specifying the return type as Foo<? extends B> gets compilation to 
succeed.

In general the reasons why I think that E is inferred here as B<Y> are 
presented below (just meaningful steps are presented). Could you please 
tell if they are correct.

1. Initial bounds set created from type parameter bounds contains 
following items as per JLS 18.1.3:

    e :< B<e> (e - is inference variable);
    e :< Object (this is added because e had no proper upper bounds);

2. Then these bounds are processed by resolution process (JLS 18.4). 
During resolution e :< Object causes instantiation e=Object according to 
following assertion from JLS 18.4:

    Otherwise, where a_i has /proper/ upper bounds U_1 , ..., U_k , T_i
    = glb(U_1 , ..., U_k )

3. Incorporating e=Object causes following new constraint to be added 
Object :< B<Object> according to following assertion from JLS 18.3.1:

    a = U and S |<:| T imply ‹S|[|a:=U|]| |<:| T|[|a:=U|]|›

5. Constraint Object :< B<Object> reduces to false causing the second 
resolution attempt to take effect according to following assertion from 
JLS 18.4:

    Otherwise, the result contains the bound /false/, so a second
    attempt is made to instantiate { a_1 , ..., a_n } by performing the
    step below.

4. Fresh type variable Y with upper bound B<Y> is introduced according 
to assertions from JLS 18.4 presented below (Y upper bound is 
glb(Object,B<e>[e:=Y]) = B<e>[e:=Y] = B<Y>):

    then let Y_1 , ..., Y_n be fresh type variables whose bounds are as
    follows:
    * For all /i/(1 ≤ /i/≤ /n/), where a_i has upper bounds U_1 , ...,
    U_k , let the upper bound of Y_i be glb(U_1 q, ..., U_k q), where
    qis the substitution |[|a_1 :=Y_1 , ..., a_n :=Y_n |]|.

5. Finally e is instantiated as a fresh type variableY with the upper 
boundB<Y> according to the following assertion from JLS 18.4:

    Otherwise, for all /i/ (1 ≤ /i/ ≤ /n/), all bounds of the form
    G|<|..., a_i , ...|>| = capture(G|<|...|>|) are removed from the
    current bound set, /_*and the bounds *_//_*a*_//_*_1 *_//_*=
    *_//_*Y_1 *_//_*, ..., *_//_*a*_//_*_n *_//_*= *_//_*Y_n *_//_*are
    incorporated.

    *_/

Thanks,
Georgiy.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20150422/e10edf8f/attachment.html>
-------------- next part --------------
class B<V> {}
class F extends B<F> {}

class Foo<E extends B<E>> {
    public Foo<E> complexMethod(E a) { return this; }
}

public class Test65  {
    public static void check() {
        Foo t4 = new Foo<>() {
            public Foo<? extends B> complexMethod(B a){ return this; }
            //public Foo<B> complexMethod(B a){ return this; } ;//this causes compilation failure
        };
    }
}


More information about the compiler-dev mailing list