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
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 instanceof
s, 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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With