如何访问方法参数的泛型类型参数上的注解?

huangapple go评论76阅读模式
英文:

How to access an annotation on a generic type parameter of a method argument?

问题

假设我想使用反射来检查以下类:

class Foo {
   void bar(List<@Important String> b) {}
}

请注意,@Important 注解位于参数本身上(否则我可以使用 Method.getParameterAnnotations()),而是位于其类型参数上(当注解声明具有 ElementType.TYPE_USE 时允许这样做)。

在 Java 11 中是否有一种方法可以读取这些注解?

英文:

Assume I want to inspect the following class using reflection:

class Foo {
   void bar(List&lt;@Important String&gt; b) {}
}

Note that the @Important annotation is not on the argument itself (then I could use Method.getParameterAnnotations()), but on its type parameter (which is allowed when the annotation is declared to have ElementType.TYPE_USE).

Is there a way to read such annotations in Java 11?

答案1

得分: 2

很遗憾,Reflection API 的这一部分很糟糕。基本类型没有必要的查询方法,也没有 Visitor API 或类似的内容。因此,任何试图进行完整内省的代码都不得不执行大量的 instanceof 检查,以处理所有可能的情况。

如果您事先知道方法的类型应该是参数化类型,并且您只想检查其第一个类型参数的注解,您可以稍微简化操作,忽略所有其他可能的情况:

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.Method;
import java.util.*;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        Method m = Foo.class.getDeclaredMethod("bar", List.class);

        var at  = m.getAnnotatedParameterTypes()[0];
        var ata = ((AnnotatedParameterizedType)at).getAnnotatedActualTypeArguments()[0];

        // 获取所有注解
        for(var a: ata.getAnnotations()) {
            System.out.println(a);
        }

        // 或者检查已知注解是否存在
        System.out.println(ata.getAnnotation(Important.class) != null);
    }

    class Foo {
        void bar(List<@Important String> b) {}
    }
}

在 Ideone 上的演示

@Important()
true

<details>
<summary>英文:</summary>

Unfortunately, this part of the Reflection API is horrible. The base types do not have the necessary query methods and there is no Visitor API or such alike. So any code trying to do a full introspection has no choice but to perform lots of `instanceof` checks, to handle all possible cases.

If you know beforehand that the method’s type should be a parameterized type and you only want to check the annotations of its first type argument, you can do it a bit simpler, ignoring all other possible cases:

```java
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.Method;
import java.util.*;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        Method m = Foo.class.getDeclaredMethod(&quot;bar&quot;, List.class);

        var at  = m.getAnnotatedParameterTypes()[0];
        var ata = ((AnnotatedParameterizedType)at).getAnnotatedActualTypeArguments()[0];

        // get all annotations
        for(var a: ata.getAnnotations()) {
            System.out.println(a);
        }

        // or check the presence of a known annotation
        System.out.println(ata.getAnnotation(Important.class) != null);
    }

    class Foo {
        void bar(List&lt;@Important String&gt; b) {}
    }
}

Demo on Ideone

@Important()
true

答案2

得分: 0

TL;DR — 请查看这个答案,讨论了类型参数,类型变量和类型参数之间的微妙差异。


冗长的版本

> „…请注意,@Important 注解在其类型参数上…

在您的 Foo 声明中…

class Foo {
   void bar(List&lt;@Important String&gt; b) {}
}

String 不是类型参数。它也不是类型变量。在您的代码片段中,String 是一个类型参数

尽管我在最初的说法中做出了更正,指出ReferenceType 类型参数不能有注解(结果证明它们可以),我将保留这些 JLS 规范,以保持谦卑…

>> 4.5.1. 参数化类型的类型参数<br /><br />
类型参数可以是引用类型或通配符。通配符在只需要关于类型参数的部分知识的情况下很有用。<br /><br />
TypeArguments:<br />
&nbsp;&nbsp;&nbsp;&lt; TypeArgumentList &gt;<br /><br />
TypeArgumentList:<br />
&nbsp;&nbsp;&nbsp;TypeArgument {, TypeArgument}<br /><br />
TypeArgument:<br />
&nbsp;&nbsp;&nbsp;ReferenceType<br /> &nbsp;&nbsp;&nbsp;Wildcard
<br /><br />
Wildcard:<br />
&nbsp;&nbsp;&nbsp;{Annotation} ? [WildcardBounds]<br /><br />
WildcardBounds:<br />
&nbsp;&nbsp;&nbsp;extends ReferenceType<br />
&nbsp;&nbsp;&nbsp;super ReferenceType

为了完整起见,以下是关于类型参数的 JLS 规范…

>> 4.4. 类型变量<br /><br />
一个 类型变量 是一个在类、接口、方法和构造函数体中用作类型的未限定标识符。<br /><br />
类型变量由泛型类、接口、方法或构造函数的 类型参数 声明引入…<br /><br />
TypeParameter:<br />
&nbsp;&nbsp;&nbsp;{TypeParameterModifier} TypeIdentifier [TypeBound]:<br /><br />
TypeParameterModifier:<br />
&nbsp;&nbsp;&nbsp;Annotation
<br /><br />

尽管我以前从未在现实中见过这种情况 — 直到今天 — 前面的 JLS 规范确认,在您的代码片段中,带注解的 String 类型参数 确实是合法的 Java。每天都有新的东西可以学习!

英文:

TL;DR — See this answer discussing the subtle differences between type parameters, type variables and type arguments.


The long-winded version

> „…Note that the @Important annotation is…on its type parameter

In your Foo declaration…

class Foo {
   void bar(List&lt;@Important String&gt; b) {}
}

String is not a type parameter. Nor is it a type variable. In your snippet there String is a type argument.

Though I stand corrected on saying originally that ReferenceType type arguments can't have annotations (turns out they can) I'll leave these JLS productions here to keep me humble

>> 4.5.1. Type Arguments of Parameterized Types<br /><br />
Type arguments may be either reference types or wildcards. Wildcards are useful
in situations where only partial knowledge about the type parameter is required.
<br /><br />
TypeArguments:<br />
&nbsp;&nbsp;&nbsp;&lt; TypeArgumentList &gt;<br /><br />
TypeArgumentList:<br />
&nbsp;&nbsp;&nbsp;TypeArgument {, TypeArgument}<br /><br />
TypeArgument:<br />
&nbsp;&nbsp;&nbsp;ReferenceType<br /> &nbsp;&nbsp;&nbsp;Wildcard
<br /><br />
Wildcard:<br />
&nbsp;&nbsp;&nbsp;{Annotation} ? [WildcardBounds]<br /><br />
WildcardBounds:<br />
&nbsp;&nbsp;&nbsp;extends ReferenceType<br />
&nbsp;&nbsp;&nbsp;super ReferenceType

For completeness, the JLS production for type parameters

>> 4.4. Type Variables<br /><br />
A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.<br /><br />
A type variable is introduced by the declaration of a type parameter of a generic class, interface, method, or constructor…<br /><br />
TypeParameter:<br />
&nbsp;&nbsp;&nbsp;{TypeParameterModifier} TypeIdentifier [TypeBound]:<br /><br />
TypeParameterModifier:<br />
&nbsp;&nbsp;&nbsp;Annotation
<br /><br />

Although I've never seen one in the wild — until today — the preceding JLS productions confirm that the annotated String type argument in your snippet is, indeed, legal Java. Learn something new everyday!

huangapple
  • 本文由 发表于 2020年10月11日 23:36:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/64305856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定