JDK 12 RFR of JDK-8058202 : AnnotatedType implementations don't override toString(), equals(), hashCode()

joe darcy joe.darcy at oracle.com
Mon Oct 8 20:45:15 UTC 2018


Hello,

Please review the changes to address:

     JDK-8058202 : AnnotatedType implementations don't override 
toString(), equals(), hashCode()
     http://cr.openjdk.java.net/~darcy/8058202.2/

Some discussion and explanation of the changes:

The java.lang.reflect.AnnotatedType interface hierarchy was added in JDK 
8 to support type annotations 
(http://openjdk.java.net/projects/type-annotations/) in core reflection. 
It offers a parallel set of interfaces to the java.lang.reflect.Type 
family of interfaces added in JDK 5. The separate AnnotatedType 
interfaces are needed since type annotations are used on type *uses* and 
not type declarations. For example, it is necessary to be able to model 
both of the types of the fields foo and bar in a case like:

     @Nonnull String foo;
     @Nullable String bar;

so clearly the same object cannot be used for both as the annotated type 
of the fields since the type annotations differ.

The implementation classes for the AnnotatedType family used in core 
reflection are in a single file AnnotatedTypeFactory.java. There are 
instantiated instances both of the base class AnnotatedTypeBaseImpl and 
its subclasses which implement the specialized subinterfaces of 
AnnotatedType for arrays, type variables, etc.

The implementation classes do *not* declare equals, hashCode, or 
toString methods, which the proposed change rectifies.

For equals and hashCode, the interfaces do *not* define how the equals 
relation should be calculated, an omission shared by the Type 
interfaces. While the default identity-is-equality is correct in some 
sense, it is not very useful as the objects returned by successive "get 
annotated return type" calls on the same method object will not be equal.

The proposed equals implementations check if the argument object 
implements the same AnnotatedType subinterface (and makes sure one of 
the subinterfaces is *not* implemented when determining equality for 
AnnotatedType implementation objects) and compares the results of 
corresponding methods. This is analogous to the equals implementations 
of the Type implementation classes found in the impl classes in the 
sun.reflect.generics.reflectiveObjects package.

The order of annotations is considered significant for equality; this is 
arguably overly strict, but I didn't think it was worthwhile to form 
sets of annotations for the purposes of equality comparison in this case.

For toString to replicate the source ordering for most kinds of types, 
the type annotations, if any, occur textually to the left of the text 
representing the type as in

     @Nonnull String

or more interestingly

         @Nonnull List<String>

the latter meaning "a nonnull list where the list contains strings."

However, this pattern is not followed for arrays. The annotated type

     @Nonnull String[]

means "an array of strings where the strings are nonnull" rather than 
"an array that is nonnull where the array contains strings." The 
rationale for this is explained in JSR 308 related documents such as:

     https://checkerframework.org/jsr308/jsr308-faq.html#syntax
https://checkerframework.org/jsr308/specification/java-annotation-design.html

A multi-dimensional array is represented as an AnnotatedArrayType whose 
component type is an array type of lower dimension, and so on, bottoming 
out with a non-array component type. The existing toString output for 
the Type objects of an array uses VM style notation like 
"[[Ljava.lang.String" for a 2D string array. This is even less helpful 
for annotated type objects and therefore toString output in the style 
usable in source code ("String [] []") will be used instead. Starting 
from an AnnotatedArrayType object for a 2D array of strings, using the 
integer value of type annotations the encounter order for the 
annotations and types is:

     @TypeAnno(2) String @TypeAnno(0) [] @TypeAnno(1) []

Therefore, for multi-dimensional arrays, the results for the nested 
dimensions are appended to the in-progress string builder while the 
results for the non-array component are pre-pended.

Thanks,

-Joe



More information about the core-libs-dev mailing list