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