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

问题描述 投票:0回答:1

我有这样的注释:

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

上课:

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

我应该如何使用 Reflection API 解析这个注释?

java reflection annotations
1个回答
3
投票

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

  1. 前提条件:注释被注释为

    @Target(ElementType.TYPE_USE)
    - 或者它根本不可能存在。

  2. 如果类型所在的位置不属于签名的一部分(例如,局部变量声明、强制转换、或

    new Foo<X>()
    中的 X 等),那么 它就消失了,泛型将被删除在这种情况下,根本没有办法引用它,你在运行时再也看不到它了。

  3. 如果类型签名的一部分(它是字段的类型,方法的返回类型,参数的类型,或者您

    extends
    implements
    的东西),那么它会被保留在运行时,但是,所有获取这些东西的常用方法,例如
    someJLReflectFieldInstance.getType()
    ,总是返回一个
    java.lang.Class
    实例,并且泛型也从这些实例中删除,所以你不能使用这些方法。

如果您乘坐#1 号船,答案立即结束:不可能

如果您乘坐 2 号船,这是可能的。首先,找到

getGenericType()
方法 - 一种变体方法,它也返回您想要的类型,但作为
Type
而不是
Class
。对于
j.l.reflect.Field
,该方法是
getGenericType()
。方法返回类型、参数类型等也存在类似的方法。然后意识到这仍然不够 - 这会为您提供
<>
中的内容,但会删除所有注释。继续寻找,您就会找到真正的赢家。对于
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()
方法,它再次返回一个无用的
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()

注意:如果你知道你在寻找什么,上面的内容并没有那么糟糕,但如果你试图处理可能类型种类的广泛数组(呵呵),你很快就会遇到一大堆混乱的代码。我不直接知道有哪些库可以提供帮助,但您可能想研究一下或写一些东西;为这些东西设置访问者模式并不太困难。

NB2:JDK20-23 及更高版本中引入的各种模式匹配开关构造是专门设计的,以避免这里的“一团糟”;你可以写这样的东西:

switch (f.getAnnotatedType()) {
  case AnnotatedParameterizedType apt -> {
    // this code runs if f's type is somethng like `List<@NonNull String>`
    // ... and variable 'apt' is available, and of type AnnotatedParameterizedType;
  }
}

您可以使用此模式匹配开关做很多事情,这只是一个非常简单的示例,说明它可以做什么以及它在这里如何有用。

© www.soinside.com 2019 - 2024. All rights reserved.