如何解析泛型中的 Java 注解?

How to parse Java annotation in generic type?

我有这样的注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface NotNull {}

还有 class:

class Main{
   List<@NotNull String> list;
}

我应该如何使用反射解析此注释 API?

注释显示在 泛型部分中。这立即意味着两件事:

  1. 如果类型位于不是签名一部分的位置(因此,局部变量声明、强制转换或 new Foo<X>() 中的 X 等 - 那么它就这样消失了,泛型在这种情况下被抹掉了。根本没有办法引用它,在运行时你再也看不到它了。

  2. 如果类型是签名的部分(它是字段的类型,方法的return类型,类型一个参数,或者你 extendsimplements) 的东西,然后在 运行 时保留它,但是,获取这些东西的所有常用方法,例如 someJLReflectFieldInstance.getType() ,总是 return 一个 java.lang.Class 实例,泛型也从这些实例中删除,所以你不能使用这些方法。

如果您在 1 号船上,答案立即结束:不可能

如果您在 2 号船上,这是可能的。首先,找到 getGenericType() 方法 - 一种变体方法,它也是 return 您想要的类型,但作为 Type 而不是 Class。对于 j.l.reflect.Field,该方法是 getGenericType()。方法 return 类型、参数类型等存在类似的方法。然后意识到这仍然不够——这让你得到了 <> 中的内容,但去掉了所有注释。继续寻找,您会找到真正的赢家。对于j.l.r.Field,即getAnnotatedType.

你现在有一个 AnnotatedType,它看起来几乎完全没用:它几乎没有任何方法。这是一个包罗万象的标记式界面,因为对于 java 中的类型有不同的想法。 ? 是这个意义上的类型,因为它可以出现在类型出现的地方:List<?>? extends Map<?, Set<String>> 也是如此。 T也是,String也是,int[]也是,double也是。而且几乎都可以标注。

通常的解决方案是转换和 instanceof 检查。 List<@NonNull String> 之类的东西是 java.lang.reflect.AnnotatedParameterizedType。因此,让我们投射并继续。这个接口有getAnnotatedActualTypeArguments()方法,return又是那个无用AnnotatedType的数组:合适;毕竟,它可能是 List<?>List<String>List<T>List<? extends Stuff> 等等。

@NonNull String 将是 java.lang.reflect.AnnotatedType.

的实例

让我们把它们放在一起。把它扔进一个文件,编译它,然后 运行 它:

import java.lang.annotation.*;
import java.util.List;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface NonNull {}

class Test {
        List<@NonNull String> example;

        public static void main(String[] args) throws Exception {
                Field f = Test.class.getDeclaredField("example");
                AnnotatedParameterizedType listType =
                  (AnnotatedParameterizedType) f.getAnnotatedType();
                AnnotatedType annType = (AnnotatedType) 
                  listType.getAnnotatedActualTypeArguments()[0];
                for (Annotation ann : annType.getAnnotations()) {
                        System.out.println("Annotation found: " + ann);
                }
        }
}


javac Test.java; java Test
> Annotation found: @NonNull()

注意:如果您知道自己在寻找什么,上面的内容还不错,但是如果您尝试处理可能类型种类繁多的数组(呵呵),您很快就会 运行变成一大堆混乱的代码。我不直接知道图书馆可以提供帮助,但您可能想调查一下或写点东西;为这些东西设置访问者模式并不难。