It appears that TYPE_USE annotations cannot be accessed through reflection when the annotated type is a nested, generic interface.
Please observe the following example:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
public class LostAnnotation {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface SomeTypeAnnotation {
}
@SomeTypeAnnotation Map<String, String> map;
@SomeTypeAnnotation Entry<String, String> entry;
public static @SomeTypeAnnotation Entry<String, String> someMethod(
@SomeTypeAnnotation Map<String, String> map,
@SomeTypeAnnotation Entry<String, String> entry) {
return null;
}
public static void main(String[] args) throws Exception {
Class<LostAnnotation> clazz = LostAnnotation.class;
Method method = clazz.getMethod("someMethod", Map.class, Entry.class);
AnnotatedType[] types = method.getAnnotatedParameterTypes();
print("map field", clazz.getDeclaredField("map").getAnnotatedType());
print("map parameter", types[0]);
print("entry field", clazz.getDeclaredField("entry").getAnnotatedType());
print("entry parameter", types[1]);
print("entry return type", method.getAnnotatedReturnType());
}
static void print(String title, AnnotatedType type) {
System.out.printf("%s: %s%n", title, Arrays.asList(type.getAnnotations()));
}
}
The expected output of the above code is
map field: [@LostAnnotation$SomeTypeAnnotation()]
map parameter: [@LostAnnotation$SomeTypeAnnotation()]
entry field: [@LostAnnotation$SomeTypeAnnotation()]
entry parameter: [@LostAnnotation$SomeTypeAnnotation()]
entry return type: [@LostAnnotation$SomeTypeAnnotation()]
However, the actual output of the above code is
map field: [@LostAnnotation$SomeTypeAnnotation()]
map parameter: [@LostAnnotation$SomeTypeAnnotation()]
entry field: []
entry parameter: []
entry return type: []
The annotation is correctly retrieved from every usage of the Map
interface. However, on every usage of the Entry
interface, be it field, return type or parameter, the annotation is lost. The only explanation that I have for this is that the Entry
interface is nested inside the Map
interface.
I ran the above example on the newest oracle JDK (8u121) on win64. Am I doing something wrong or could this be a bug?
My Annotation is nested for readability. Making it a top-level interface doesn't change anything.
This has been already spotted in SO: Why annotation on generic type argument is not visible for nested type?
Answer is bug has been submitted but in lower priority as those cases do not appear frequently.
Still, you can use ByteBuddy to parse the bytecode and retrieve the annotated type.
ASM works too, from my experience, and I suspect any bytecode parser would work around this bug.
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