2

Assume I want to inspect the following class using reflection:

class Foo {
   void bar(List<@Important String> 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?

Landei
  • 54,104
  • 13
  • 100
  • 195
  • not sure if I get the question right, but if you can access the method, you can access the parameter annotations as well, isn't it? – Naman Oct 11 '20 at 17:04
  • @Naman this is not an annotation directly on the method argument, but on the type parameter of the method argument, As I wrote, you don't get this annotation by calling `Method.getParameterAnnotations`. – Landei Oct 11 '20 at 17:35

2 Answers2

2

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:

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];

        // 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<@Important String> b) {}
    }
}

Demo on Ideone

@Important()
true
Holger
  • 285,553
  • 42
  • 434
  • 765
0

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<@Important String> 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

Type arguments may be either reference types or wildcards. Wildcards are useful in situations where only partial knowledge about the type parameter is required.

TypeArguments:
   < TypeArgumentList >

TypeArgumentList:
   TypeArgument {, TypeArgument}

TypeArgument:
   ReferenceType
   Wildcard

Wildcard:
   {Annotation} ? [WildcardBounds]

WildcardBounds:
   extends ReferenceType
   super ReferenceType

For completeness, the JLS production for type parameters

4.4. Type Variables

A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.

A type variable is introduced by the declaration of a type parameter of a generic class, interface, method, or constructor…

TypeParameter:
   {TypeParameterModifier} TypeIdentifier [TypeBound]:

TypeParameterModifier:
   Annotation

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!

deduper
  • 1,944
  • 9
  • 22
  • 2
    The OP mixed up the formal terms “type parameter” and “type argument”, but confusing “parameter” and “argument” is quite common. Regarding the validity of annotations for type arguments, you should follow the [*`ReferenceType`*](https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-ReferenceType) link… – Holger Oct 12 '20 at 08:27