How to parse Java annotation in generic type?



I have annotation like this:

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

And have a class:

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

How should i parse this annotation using Reflection API?

Answer

The annotation shows up within the generics part. This immediately means two things:

  1. If the type is in a place that isn’t part of a signature (so, a local variable declaration, or a cast, or the X in new Foo<X>(), etc – then it is just gone, generics are erased in such circumstances. There is simply no way to refer to it, you can’t see it anymore at runtime.

  2. If the type is part of a signature (it is the type of a field, the return type of a method, the type of a parameter, or something you extends or implements), then it is retained at runtime, but, all the usual methods to get this stuff, such as someJLReflectFieldInstance.getType(), always return a java.lang.Class instance, and generics is eliminated from these as well, so you can’t use these methods.

If you’re in boat #1, the answer is immediately over: impossible.

If you’re in boat #2, this is possible. First, find the getGenericType() method – a variant method that also returns the type you want, but as a Type and not as a Class. For j.l.reflect.Field, that method is getGenericType(). Similar methods exist for method return types, parameter types, etcetera. Then realize this still isn’t enough – that gets you the stuff in the <>, but strips out all annotations. Keep looking and you find the true winner. For j.l.r.Field, that is getAnnotatedType.

You now have an AnnotatedType, which seems almost entirely useless: It has virtually no methods. It’s a catch-all marker-esque interface, because there are really different ideas on what types are in java. ? is a type in this sense, because it can show up in a place types show up: List<?>. So is ? extends Map<?, Set<String>>. So is T, and so is String, and so is int[], and so double. And they can almost all be annotated.

The usual solution is casting and instanceof checks. Something like List<@NonNull String> is a java.lang.reflect.AnnotatedParameterizedType. So let’s cast and continue with that. This interface has the getAnnotatedActualTypeArguments() method, which returns an array of that useless AnnotatedType again: Appropriate; after all, it could be List<?>, List<String>, List<T>, List<? extends Stuff>, etcetera.

@NonNull String would be an instanceof java.lang.reflect.AnnotatedType.

Let’s put it all together. Toss this in a file, compile it, and run it:

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()

NB: If you know what you are looking for, the above isn’t so bad, but if you’re trying to process a wide array (heh) of possible type kinds, you soon run into a gigantic soupy mess of code. I’m not directly aware of libraries to help out, but you may want to look into it or write something; it’s not too difficult to set up a visitor pattern for this stuff.



Source: stackoverflow