FULL PRODUCT VERSION :
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux d0074211 4.13.11 #1 SMP Thu Nov 2 22:32:48 CET 2017 x86_64 GNU/Linux
EXTRA RELEVANT SYSTEM CONFIGURATION :
Verified the bug on multiple OSes (Linux, OS X, Windows) and JDK 8.x and 9.x versions
A DESCRIPTION OF THE PROBLEM :
Consider this simple class:
public class SomeClass<T> {
public <@A1 S extends @A2 T> @A3 S myMethod() { ...}
}
@A2 annotation on the bound T is inaccessible via reflection. See below for details.
Assuming myMethod is SomeClass.class.getDeclaredMethod("myMethod"). Type casts removed for readability.
myMethod.getGenericReturnType().getAnnotations() - returns @A1 (expected)
myMethod.getGenericReturnType().getBounds()[0].getAnnotations() - returns nothing (no declaration annotation on T)
myMethod.getAnnotatedReturnType().getAnnotations() - returns @A3 (expected)
myMethod.getAnnotatedReturnType().getAnnotatedBounds()[0].getAnnotations() - returns @A2
See https://stackoverflow.com/questions/47175573/how-to-get-type-use-annotations-on-a-generic-bound for a better formatted description, discussion and analysis
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a generic class containing a method with an annotated bounded type variable as the return type.
E.g.
public class SomeClass<T> {
public <@A1 S extends @A2 T> @A3 S myMethod() { ...}
}
2. Try retrieving the @A2 annotation via reflection:
Method myMethod = SomeClass.class.getDeclaredMethod("myMethod");
Annotation[] annotations = myMethod.getGenericReturnType().getBounds()[0].getAnnotations(); // declaration annotation on T
Annotation[] annotations2 = myMethod.getAnnotatedReturnType().getAnnotatedBounds()[0].getAnnotations(); // type use annotation on T's use as upper(extends) bound
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
At least one of the annotation arrays from above (annotations and annotations2) should contain the @A2 instance
ACTUAL -
Both methods return no annotations
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package io.leangen.graphql.jimmy;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedTypeVariable;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
public class AnnotatedBoundTest {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface A1 {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface A2 {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface A3 {
}
public class SomeClass<T> {
<@A1 S extends @A2 T> @A3 S myMethod() { return null;}
}
public static void main(String[] args) throws NoSuchMethodException {
Method myMethod = SomeClass.class.getDeclaredMethod("myMethod");
Annotation[] annotations = ((TypeVariable) ((TypeVariable) myMethod.getGenericReturnType()).getBounds()[0]).getAnnotations();
Annotation[] annotations2 = ((AnnotatedTypeVariable) myMethod.getAnnotatedReturnType()).getAnnotatedBounds()[0].getAnnotations();
assert annotations.length == 1; // fails, no declaration annotation on T
assert annotations2.length == 1; // succeeds
}
}
---------- END SOURCE ----------