TYPE_USE annotations get lost when type is nested,

2020-08-17 17:47发布

问题:

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.

回答1:

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.



回答2:

I do not know if I count as a credible source (definitely not an official one), but IMO this is a bug or at least a shortcoming (not fully implemented) in the JDK.

You are not doing anything wrong - or we are both wrong, which is still a possibility.

As Bertrand Russell said:

The fact that an opinion has been widely held is no evidence whatever that it is not utterly absurd.

;-)