Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get TYPE_USE annotations on a generic bound

I have this case:

public class SomeClass<T> {
    public <@A1 S extends @A2 T> @A3 S myMethod() { ...}
}

and I'm trying to get the @A2 annotation on the bound T.
This is what I'm seeing, assuming myMethod is SomeClass.class.getDeclaredMethod("myMethod"). Type casts removed for readability.

  • myMethod.getGenericReturnType().getAnnotations() returns @A1 (as expected, I guess)
  • myMethod.getGenericReturnType().getBounds()[0].getAnnotations() returns nothing (?? shoudn't it be @A2 ??)
  • myMethod.getAnnotatedReturnType().getAnnotations() returns @A3 (as expected, I guess)
  • myMethod.getAnnotatedReturnType().getAnnotatedBounds()[0].getAnnotations() returns nothing (as excepted, I guess)

It seems like @A2 got lost... and that doesn't sound sensible. Am I doing something wrong or is this indeed some bizarre behavior?

UPDATE: It is indeed a JDK bug, which I reported and it got accepted as JDK-8191466

like image 396
kaqqao Avatar asked Nov 08 '17 09:11

kaqqao


1 Answers

All type annotations can be retrieved through the AnnotatedType hierarchy, except for the TypeVariable, whose annotations can be retrieve through AnnotatedTypeVariable.getType() which will be an instance of TypeVariable which extends AnnotatedElement now and happens to be identical with the object returned by Method.getGenericReturnType().

So you can retrieve all information, e.g.

AnnotatedType art = myMethod.getAnnotatedReturnType();
System.out.print(
    Arrays.toString(art.getAnnotations())+" "+art.getType().getTypeName()+" -> ");
final boolean typeVariable = art instanceof AnnotatedTypeVariable;
if (typeVariable) System.out.print('<');
System.out.print(Arrays.toString(((AnnotatedElement)art.getType()).getAnnotations()) + " ");
System.out.print(art.getType().getTypeName());
if (typeVariable) {
    AnnotatedTypeVariable atv = (AnnotatedTypeVariable)art;
    AnnotatedType[] annotatedBounds = atv.getAnnotatedBounds();
    if (annotatedBounds.length > 0) {
        System.out.print(" extends ");
        for (AnnotatedType aBound: annotatedBounds) {
            System.out.print(Arrays.toString(aBound.getAnnotations()) + " ");
            System.out.print(aBound.getType().getTypeName() + ", ");
        }
    }
    System.out.println(">");
}

will print

[@A3()] S -> <[@A1()] S extends [@A2()] T, >

When your invocation of ((AnnotatedTypeVariable)myMethod.getAnnotatedReturnType()) .getAnnotatedBounds()[0].getAnnotations() does not provide @A2, you should recheck that A2 truly has @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE).

Of course, that’s the “simplified” version handling only your specific method.

To handle all possible cases, you will need much more instanceofs, dealing with these disjunct type hierarchies of Type and AnnotatedType, especially when processing annotated parameterized types and annotated generic array types.

As said, TypeVariable is different, as it extends AnnotatedElement and also has the getAnnotatedBounds() method. So in this specific case, an alternative way to process the method is

List<TypeVariable<?>> typeParameters = Arrays.asList(myMethod.getTypeParameters());

for (TypeVariable<?> tv: typeParameters) {
    System.out.print("< "+Arrays.toString(tv.getAnnotations())+" "+tv.getName());
    AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();
    if (annotatedBounds.length > 0) {
        System.out.print(" extends ");
        for (AnnotatedType aBound: annotatedBounds) {
            System.out.print(Arrays.toString(aBound.getAnnotations()) + " ");
            System.out.print(aBound.getType().getTypeName() + ", ");
        }
    }
    System.out.print("> ");
}

AnnotatedType art = myMethod.getAnnotatedReturnType();
System.out.print(Arrays.toString(art.getAnnotations()) + " ");
int ix = typeParameters.indexOf(art.getType());
if (ix >= 0) System.out.print("[ref to type parameter #" + ix + "] ");
System.out.println(art.getType().getTypeName());
like image 85
Holger Avatar answered Oct 15 '22 01:10

Holger