53

Suppose I have this annotation class


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodXY {
    public int x();
    public int y();
}

public class AnnotationTest {
    @MethodXY(x=5, y=5)
    public void myMethodA(){ ... }

    @MethodXY(x=3, y=2)
    public void myMethodB(){ ... }
}

So is there a way to look into an object, "seek" out the method with the @MethodXY annotation, where its element x = 3, y = 2, and invoke it?

Thanks

nobody
  • 2,709
  • 6
  • 35
  • 37

3 Answers3

84

Here is a method, which returns methods with specific annotations:

public static List<Method> getMethodsAnnotatedWith(final Class<?> type, final Class<? extends Annotation> annotation) {
    final List<Method> methods = new ArrayList<Method>();
    Class<?> klass = type;
    while (klass != Object.class) { // need to traverse a type hierarchy in order to process methods from super types
        // iterate though the list of methods declared in the class represented by klass variable, and add those annotated with the specified annotation
        for (final Method method : klass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(annotation)) {
                Annotation annotInstance = method.getAnnotation(annotation);
                // TODO process annotInstance
                methods.add(method);
            }
        }
        // move to the upper class in the hierarchy in search for more methods
        klass = klass.getSuperclass();
    }
    return methods;
}

It can be easily modified to your specific needs. Pls note that the provided method traverses class hierarchy in order to find methods with required annotations.

Here is a method for your specific needs:

public static List<Method> getMethodsAnnotatedWithMethodXY(final Class<?> type) {
    final List<Method> methods = new ArrayList<Method>();
    Class<?> klass = type;
    while (klass != Object.class) { // need to iterated thought hierarchy in order to retrieve methods from above the current instance
        // iterate though the list of methods declared in the class represented by klass variable, and add those annotated with the specified annotation
        for (final Method method : klass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MethodXY.class)) {
                MethodXY annotInstance = method.getAnnotation(MethodXY.class);
                if (annotInstance.x() == 3 && annotInstance.y() == 2) {         
                    methods.add(method);
                }
            }
        }
        // move to the upper class in the hierarchy in search for more methods
        klass = klass.getSuperclass();
    }
    return methods;
}

For invocation of the found method(s) pls refer a tutorial. One of the potential difficulties here is the number of method arguments, which could vary between found methods and thus requiring some additional processing.

01es
  • 5,362
  • 1
  • 31
  • 40
  • 7
    I guess every code sample I have seen so far all involves "scanning" all methods – nobody Jul 06 '11 at 14:29
  • Just a heads up, annotation might be null in the line Annotation annotInstance = method.getAnnotation(annotation); – kliron Aug 17 '15 at 07:01
  • @kliron There is guard `if (method.isAnnotationPresent(MethodXY.class)) ...` so why `null`? – 01es Aug 17 '15 at 23:36
  • I meant your first method `getMethodsAnnotatedWith`, line 9. – kliron Aug 18 '15 at 05:53
  • @kliron Thank you for bringing this to my attention -- it was a quick copy paste and in this case that condition was just wrong. The code snipped has been adjusted. – 01es Aug 19 '15 at 05:57
  • It can still throw a `NullPointerException` if annotation is null when calling `isAnnotationPresent` (line 8). It's fine if the caller is expected to deal with it. If this is not intended, one should use a guard to test that the method is not called with a null annotation argument. For reference: http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AnnotatedElement.html#isAnnotationPresent(java.lang.Class) – kliron Aug 19 '15 at 06:20
  • @kliron it surely can, but this is really up to the end developer to apply defensive practices... – 01es Aug 19 '15 at 06:47
  • Why the ArrayList here: final List allMethods = new ArrayList(Arrays.asList(klass.getDeclaredMethods())); The for loop can iterate over the array. Method[] allMethods = klass.getDeclaredMethods(); – ezzadeen Mar 29 '20 at 12:15
  • @ezzadeen it is hard to say why a list was used at the time, but you're right -- there is no need for it in this specific context and the code can be simplified. – 01es Apr 07 '20 at 06:54
4

try this code sample:

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.reflect.InvocationTargetException;

class AnotTest {
    public static void main(String... args) {
        AnnotationTest at = new AnnotationTest();
        for (Method m : at.getClass().getMethods()) {
           MethodXY mXY = (MethodXY)m.getAnnotation(MethodXY.class);
           if (mXY != null) {
               if (mXY.x() == 3 && mXY.y() == 2){
                   try {
                       m.invoke(at);
                   } catch (IllegalAccessException e) {
                       //do nothing;
                   } catch (InvocationTargetException o) {
                       //do nothing;
                   }
               }
           }
        }
    }
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    static public @interface MethodXY {
        public int x();
        public int y();
    }

    static class AnnotationTest {
        @MethodXY(x=5, y=5)
        public void myMethodA() {
            System.out.println("boo");
        }

        @MethodXY(x=3, y=2)
        public void myMethodB() {
            System.out.println("foo");
        }
    }
}
dhblah
  • 9,751
  • 12
  • 56
  • 92
  • 1
    @tilaprimera creating new instance of AnnotationTest, taking all methods of the defining class, iterating through methods, on each of them trying to get annotation named `MethodXY`, if we can obtain it, then we're checking what `x` and `y` values it has, if they're 3 and 2 respectively, we're invoking that method. – dhblah May 20 '15 at 14:15
1
private static final Method getMethodByAnnotation(Class<?> cla, Class<? extends Annotation> annotation,
        int methodIndex) {
    Stream<Method> stream = Arrays.stream(cla.getDeclaredMethods())
            .filter(m -> m.isAnnotationPresent(annotation));
    return methodIndex == 0 ? stream.findFirst().get() : stream.toArray(Method[]::new)[methodIndex];
}
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 22 '22 at 06:34