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

2018-10-12 Thread Werner Dietl
Hi Joe, all,

the logic looks good to me.

In the tests I'm wondering whether to include an annotated wildcard
bound. There is:

307 public @AnnotType(11) Set<@AnnotType(13) ? extends Number>
fooNumberSet2() {return null;}

but nothing like

Set fooNumberSet2() {return null;}

I wouldn't expect problems for this, but a test would exercise some of
the code that gets added.

Best,
cu, WMD.




On Wed, Oct 10, 2018 at 2:40 AM Joel Borggrén-Franck
 wrote:
>
> Hej Joe,
>
> New version looks good!
>
> Thanks for the explanations, makes sense to me.
>
> Cheers
> /Joel
>
> On Wed, 10 Oct 2018 at 08:27, joe darcy  wrote:
>>
>> Hi Joel,
>>
>> Thanks for the review; slightly revised version at
>>
>>  http://cr.openjdk.java.net/~darcy/8058202.3/
>>
>> Comments below.
>>
>>
>> On 10/9/2018 11:00 AM, Joel Borggrén-Franck wrote:
>> > Hi Joe,
>> >
>> > Good to see this happening. In general looks good to me, a few nits below.
>> >
>> > AnnotatedTypeBaseImpl contains comments indicating from which
>> > superclass or interface the overridden methods comes. I'd either add
>> > // Object at line 207 or delete line 145 and 177 for consistency.
>>
>> Okay; comments added to follow that local convention.
>>
>> >
>> > Even though this isn't performance critical by far the allocation at
>> > line 215 still bugs me a bit given that the common case will most
>> > certainly be no annotations.
>>
>> Sure; refactored to avoid the allocation.
>>
>> >
>> > Why the inverted logic line 250-253, IIRC you should be able to test
>> > if it is an AnnotatedBaseTypeImpl, or?
>>
>> I was aiming to avoid testing for the impl class directly to not
>> directly tie the classes to this particular implementation of the
>> AnnotatedType hierarchy. However, inter-operability based on the specs
>> would need the equals and hashCode behavior to be defined.
>>
>> >
>> > For equalsTypeAndAnnotations and baseHashCode lines 232-244 equals
>> > test for owner type equality while hashcode doesn't include owner
>> > type. I must confess I no longer remember the equals-hashCode contract
>> > but I assume this is intentional.
>>
>> Good catch; equals and hashCode checks made consistent.
>>
>> Some refactoring and hardening of the test included too.
>>
>> Delta-diffs below.
>>
>> Thanks,
>>
>> -Joe
>>
>> diff
>> webrev.2/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
>> webrev/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
>>
>> 2c2
>> <  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights
>> reserved.
>> ---
>>  >  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights
>> reserved.
>> 207c207
>> < @Override
>> ---
>>  > @Override // java.lang.Object
>> 215d214
>> < StringJoiner sj = new StringJoiner(" ");
>> 216a216
>>  > StringJoiner sj = new StringJoiner(" ");
>> 228,229c228,231
>> < }
>> < return sj.toString();
>> ---
>>  > return sj.toString();
>>  > } else {
>>  > return "";
>>  > }
>> 244c246,247
>> < Objects.hash((Object[])getAnnotations());
>> ---
>>  > Objects.hash((Object[])getAnnotations()) ^
>>  > Objects.hash(getAnnotatedOwnerType());
>>
>> diff
>> webrev.2/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java
>> webrev/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java
>>
>> 142d141
>> <
>> 157,168c156,158
>> < AnnotatedType annotType1 = m.getAnnotatedReturnType();
>> < AnnotatedType annotType2 = m.getAnnotatedReturnType();
>> <
>> < boolean valid = annotType1.equals(annotType2);
>> <
>> < if (!valid) {
>> < errors++;
>> < System.err.println(annotType1);
>> < System.err.println(" is not equal to ");
>> < System.err.println(annotType2);
>> < System.err.println();
>> < }
>> ---
>>  > checkTypesForEquality(m.getAnnotatedReturnType(),
>>  >   m.getAnnotatedReturnType(),
>>  >   true);
>> 171a162,185
>>  > private static void checkTypesForEquality(AnnotatedType annotType1,
>>  >   AnnotatedType annotType2,
>>  >   boolean expected) {
>>  > boolean comparison = annotType1.equals(annotType2);
>>  >
>>  > if (comparison) {
>>  > int hash1 = annotType1.hashCode();
>>  > int hash2 = annotType1.hashCode();
>>  > if (hash1 != hash2) {
>>  > errors++;
>>  > System.err.format("Equal AnnotatedTypes with unequal hash
>> codes: %n%s%n%s%n",
>>  >   annotType1.toString(), annotType2.toString());
>>  > }
>>  > }
>>  >
>>  > if (comparison != expected) {
>>  > errors++;
>>  > System.err.println(annotType1);
>>  > 

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

2018-10-11 Thread joe darcy

Hi Werner,

On 10/10/2018 1:23 PM, Werner Dietl wrote:

Hi Joe, all,

the logic looks good to me.

In the tests I'm wondering whether to include an annotated wildcard
bound. There is:

307 public @AnnotType(11) Set<@AnnotType(13) ? extends Number>
fooNumberSet2() {return null;}

but nothing like

Set fooNumberSet2() {return null;}

I wouldn't expect problems for this, but a test would exercise some of
the code that gets added.


You were correct to probe at the bounds on the wildcard components; the 
code that was reviewed and pushed does not print annotations on the bounds.


I'll work on an update to handle this and similar cases where there are 
embedded types that could have annotations.


Thanks,

-Joe



Best,
cu, WMD.




On Wed, Oct 10, 2018 at 2:40 AM Joel Borggrén-Franck
 wrote:

Hej Joe,

New version looks good!

Thanks for the explanations, makes sense to me.

Cheers
/Joel

On Wed, 10 Oct 2018 at 08:27, joe darcy  wrote:

Hi Joel,

Thanks for the review; slightly revised version at

  http://cr.openjdk.java.net/~darcy/8058202.3/

Comments below.


On 10/9/2018 11:00 AM, Joel Borggrén-Franck wrote:

Hi Joe,

Good to see this happening. In general looks good to me, a few nits below.

AnnotatedTypeBaseImpl contains comments indicating from which
superclass or interface the overridden methods comes. I'd either add
// Object at line 207 or delete line 145 and 177 for consistency.

Okay; comments added to follow that local convention.


Even though this isn't performance critical by far the allocation at
line 215 still bugs me a bit given that the common case will most
certainly be no annotations.

Sure; refactored to avoid the allocation.


Why the inverted logic line 250-253, IIRC you should be able to test
if it is an AnnotatedBaseTypeImpl, or?

I was aiming to avoid testing for the impl class directly to not
directly tie the classes to this particular implementation of the
AnnotatedType hierarchy. However, inter-operability based on the specs
would need the equals and hashCode behavior to be defined.


For equalsTypeAndAnnotations and baseHashCode lines 232-244 equals
test for owner type equality while hashcode doesn't include owner
type. I must confess I no longer remember the equals-hashCode contract
but I assume this is intentional.

Good catch; equals and hashCode checks made consistent.

Some refactoring and hardening of the test included too.

Delta-diffs below.

Thanks,

-Joe

diff
webrev.2/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
webrev/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java

2c2
<  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights
reserved.
---
  >  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights
reserved.
207c207
< @Override
---
  > @Override // java.lang.Object
215d214
< StringJoiner sj = new StringJoiner(" ");
216a216
  > StringJoiner sj = new StringJoiner(" ");
228,229c228,231
< }
< return sj.toString();
---
  > return sj.toString();
  > } else {
  > return "";
  > }
244c246,247
< Objects.hash((Object[])getAnnotations());
---
  > Objects.hash((Object[])getAnnotations()) ^
  > Objects.hash(getAnnotatedOwnerType());

diff
webrev.2/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java
webrev/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java

142d141
<
157,168c156,158
< AnnotatedType annotType1 = m.getAnnotatedReturnType();
< AnnotatedType annotType2 = m.getAnnotatedReturnType();
<
< boolean valid = annotType1.equals(annotType2);
<
< if (!valid) {
< errors++;
< System.err.println(annotType1);
< System.err.println(" is not equal to ");
< System.err.println(annotType2);
< System.err.println();
< }
---
  > checkTypesForEquality(m.getAnnotatedReturnType(),
  >   m.getAnnotatedReturnType(),
  >   true);
171a162,185
  > private static void checkTypesForEquality(AnnotatedType annotType1,
  >   AnnotatedType annotType2,
  >   boolean expected) {
  > boolean comparison = annotType1.equals(annotType2);
  >
  > if (comparison) {
  > int hash1 = annotType1.hashCode();
  > int hash2 = annotType1.hashCode();
  > if (hash1 != hash2) {
  > errors++;
  > System.err.format("Equal AnnotatedTypes with unequal hash
codes: %n%s%n%s%n",
  >   annotType1.toString(), annotType2.toString());
  > }
  > }
  >
  > if (comparison != expected) {
  > errors++;
  > System.err.println(annotType1);
  > System.err.println(expected ? " is not 

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

2018-10-10 Thread Joel Borggrén-Franck
Hej Joe,

New version looks good!

Thanks for the explanations, makes sense to me.

Cheers
/Joel

On Wed, 10 Oct 2018 at 08:27, joe darcy  wrote:

> Hi Joel,
>
> Thanks for the review; slightly revised version at
>
>  http://cr.openjdk.java.net/~darcy/8058202.3/
>
> Comments below.
>
>
> On 10/9/2018 11:00 AM, Joel Borggrén-Franck wrote:
> > Hi Joe,
> >
> > Good to see this happening. In general looks good to me, a few nits
> below.
> >
> > AnnotatedTypeBaseImpl contains comments indicating from which
> > superclass or interface the overridden methods comes. I'd either add
> > // Object at line 207 or delete line 145 and 177 for consistency.
>
> Okay; comments added to follow that local convention.
>
> >
> > Even though this isn't performance critical by far the allocation at
> > line 215 still bugs me a bit given that the common case will most
> > certainly be no annotations.
>
> Sure; refactored to avoid the allocation.
>
> >
> > Why the inverted logic line 250-253, IIRC you should be able to test
> > if it is an AnnotatedBaseTypeImpl, or?
>
> I was aiming to avoid testing for the impl class directly to not
> directly tie the classes to this particular implementation of the
> AnnotatedType hierarchy. However, inter-operability based on the specs
> would need the equals and hashCode behavior to be defined.
>
> >
> > For equalsTypeAndAnnotations and baseHashCode lines 232-244 equals
> > test for owner type equality while hashcode doesn't include owner
> > type. I must confess I no longer remember the equals-hashCode contract
> > but I assume this is intentional.
>
> Good catch; equals and hashCode checks made consistent.
>
> Some refactoring and hardening of the test included too.
>
> Delta-diffs below.
>
> Thanks,
>
> -Joe
>
> diff
> webrev.2/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
>
> webrev/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
>
>
> 2c2
> <  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights
> reserved.
> ---
>  >  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights
> reserved.
> 207c207
> < @Override
> ---
>  > @Override // java.lang.Object
> 215d214
> < StringJoiner sj = new StringJoiner(" ");
> 216a216
>  > StringJoiner sj = new StringJoiner(" ");
> 228,229c228,231
> < }
> < return sj.toString();
> ---
>  > return sj.toString();
>  > } else {
>  > return "";
>  > }
> 244c246,247
> < Objects.hash((Object[])getAnnotations());
> ---
>  > Objects.hash((Object[])getAnnotations()) ^
>  > Objects.hash(getAnnotatedOwnerType());
>
> diff
> webrev.2/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java
>
> webrev/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java
>
>
> 142d141
> <
> 157,168c156,158
> < AnnotatedType annotType1 = m.getAnnotatedReturnType();
> < AnnotatedType annotType2 = m.getAnnotatedReturnType();
> <
> < boolean valid = annotType1.equals(annotType2);
> <
> < if (!valid) {
> < errors++;
> < System.err.println(annotType1);
> < System.err.println(" is not equal to ");
> < System.err.println(annotType2);
> < System.err.println();
> < }
> ---
>  > checkTypesForEquality(m.getAnnotatedReturnType(),
>  >   m.getAnnotatedReturnType(),
>  >   true);
> 171a162,185
>  > private static void checkTypesForEquality(AnnotatedType annotType1,
>  >   AnnotatedType annotType2,
>  >   boolean expected) {
>  > boolean comparison = annotType1.equals(annotType2);
>  >
>  > if (comparison) {
>  > int hash1 = annotType1.hashCode();
>  > int hash2 = annotType1.hashCode();
>  > if (hash1 != hash2) {
>  > errors++;
>  > System.err.format("Equal AnnotatedTypes with unequal hash
> codes: %n%s%n%s%n",
>  >   annotType1.toString(), annotType2.toString());
>  > }
>  > }
>  >
>  > if (comparison != expected) {
>  > errors++;
>  > System.err.println(annotType1);
>  > System.err.println(expected ? " is not equal to " : " is
> equal to ");
>  > System.err.println(annotType2);
>  > System.err.println();
>  > }
>  > }
>  >
> 184,196c198,200
> < AnnotatedType annotType1 =
> methods[i].getAnnotatedReturnType();
> < AnnotatedType annotType2 =
> methods[j].getAnnotatedReturnType();
> <
> < boolean valid = !annotType1.equals(annotType2);
> <
> < if (!valid) {
> < errors++;
> < System.err.println(annotType1);
> <

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

2018-10-10 Thread joe darcy

Hi Joel,

Thanks for the review; slightly revised version at

    http://cr.openjdk.java.net/~darcy/8058202.3/

Comments below.


On 10/9/2018 11:00 AM, Joel Borggrén-Franck wrote:

Hi Joe,

Good to see this happening. In general looks good to me, a few nits below.

AnnotatedTypeBaseImpl contains comments indicating from which
superclass or interface the overridden methods comes. I'd either add
// Object at line 207 or delete line 145 and 177 for consistency.


Okay; comments added to follow that local convention.



Even though this isn't performance critical by far the allocation at
line 215 still bugs me a bit given that the common case will most
certainly be no annotations.


Sure; refactored to avoid the allocation.



Why the inverted logic line 250-253, IIRC you should be able to test
if it is an AnnotatedBaseTypeImpl, or?


I was aiming to avoid testing for the impl class directly to not 
directly tie the classes to this particular implementation of the 
AnnotatedType hierarchy. However, inter-operability based on the specs 
would need the equals and hashCode behavior to be defined.




For equalsTypeAndAnnotations and baseHashCode lines 232-244 equals
test for owner type equality while hashcode doesn't include owner
type. I must confess I no longer remember the equals-hashCode contract
but I assume this is intentional.


Good catch; equals and hashCode checks made consistent.

Some refactoring and hardening of the test included too.

Delta-diffs below.

Thanks,

-Joe

diff 
webrev.2/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java 
webrev/raw_files/new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java 


2c2
<  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights 
reserved.

---
>  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights 
reserved.

207c207
< @Override
---
> @Override // java.lang.Object
215d214
< StringJoiner sj = new StringJoiner(" ");
216a216
>         StringJoiner sj = new StringJoiner(" ");
228,229c228,231
< }
< return sj.toString();
---
>         return sj.toString();
> } else {
>         return "";
>         }
244c246,247
< Objects.hash((Object[])getAnnotations());
---
> Objects.hash((Object[])getAnnotations()) ^
>         Objects.hash(getAnnotatedOwnerType());

diff 
webrev.2/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java 
webrev/raw_files/new/test/jdk/java/lang/annotation/typeAnnotations/TestObjectMethods.java 


142d141
<
157,168c156,158
< AnnotatedType annotType1 = m.getAnnotatedReturnType();
< AnnotatedType annotType2 = m.getAnnotatedReturnType();
<
< boolean valid = annotType1.equals(annotType2);
<
< if (!valid) {
< errors++;
< System.err.println(annotType1);
< System.err.println(" is not equal to ");
< System.err.println(annotType2);
< System.err.println();
< }
---
>         checkTypesForEquality(m.getAnnotatedReturnType(),
>                   m.getAnnotatedReturnType(),
>                   true);
171a162,185
> private static void checkTypesForEquality(AnnotatedType annotType1,
>                       AnnotatedType annotType2,
>                       boolean expected) {
>     boolean comparison = annotType1.equals(annotType2);
>
>     if (comparison) {
>         int hash1 = annotType1.hashCode();
>         int hash2 = annotType1.hashCode();
>         if (hash1 != hash2) {
>         errors++;
>         System.err.format("Equal AnnotatedTypes with unequal hash 
codes: %n%s%n%s%n",

>                   annotType1.toString(), annotType2.toString());
>         }
>     }
>
>     if (comparison != expected) {
>         errors++;
>         System.err.println(annotType1);
>         System.err.println(expected ? " is not equal to " : " is 
equal to ");

>         System.err.println(annotType2);
>         System.err.println();
>     }
> }
>
184,196c198,200
< AnnotatedType annotType1 = 
methods[i].getAnnotatedReturnType();
< AnnotatedType annotType2 = 
methods[j].getAnnotatedReturnType();

<
< boolean valid = !annotType1.equals(annotType2);
<
< if (!valid) {
< errors++;
< System.err.println(annotType1);
< System.err.println(" is equal to ");
< System.err.println(annotType2);
< System.err.println();
< }
<
---
> checkTypesForEquality(methods[i].getAnnotatedReturnType(),
>                       methods[j].getAnnotatedReturnType(),
>                       false);
209,210c213
< Method[] methods1  = clazz1.getDeclaredMethods();
< Method[] methods2 = 

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

2018-10-09 Thread Joel Borggrén-Franck
Hi Joe,

Good to see this happening. In general looks good to me, a few nits below.

AnnotatedTypeBaseImpl contains comments indicating from which
superclass or interface the overridden methods comes. I'd either add
// Object at line 207 or delete line 145 and 177 for consistency.

Even though this isn't performance critical by far the allocation at
line 215 still bugs me a bit given that the common case will most
certainly be no annotations.

Why the inverted logic line 250-253, IIRC you should be able to test
if it is an AnnotatedBaseTypeImpl, or?

For equalsTypeAndAnnotations and baseHashCode lines 232-244 equals
test for owner type equality while hashcode doesn't include owner
type. I must confess I no longer remember the equals-hashCode contract
but I assume this is intentional.

Cheers
/Joel


On Mon, Oct 8, 2018 at 10:45 PM joe darcy  wrote:
>
> 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
>
> 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 

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

2018-10-08 Thread joe darcy

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

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