3

So, my problem is the following, I'm using annotations to tag the methods of a class.

My main annotation is @Action and I need a stronger annotation for specific methods that is @SpecificAction.

All the methods annotated with @SpecificAction must be annotated as @Action. My idea is to have @SpecificAction annotated with @Action.

@Action
[other irrelevant annotations]
public @interface SpecificAction{}

with

@SpecificAction
public void specificMethod(){}

I would expect specificMethod.isAnnotationPresent(Action.class) to be true, but it isn't.

How could I make it so that the @Action annotation is "inherited"?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
ldebroux
  • 147
  • 1
  • 9

1 Answers1

3

As @assylias's link says, the annotations can't be inherited, but you can use the composition, and search recursively your target annotation like this:

public static class AnnotationUtil {

    private static <T extends Annotation> boolean containsAnnotation(Class<? extends Annotation> annotation, Class<T> annotationTypeTarget, Set<Class<? extends Annotation>> revised) {
        boolean result = !revised.contains(annotation);
        if (result && annotationTypeTarget != annotation) {
            Set<Class<? extends Annotation>> nextRevised = new HashSet<>(revised);
            nextRevised.add(annotation);
            result = Arrays.stream(annotation.getAnnotations()).anyMatch(a -> containsAnnotation(a.annotationType(), annotationTypeTarget, nextRevised));
        }
        return result;
    }

    public static <T extends Annotation> boolean containsAnnotation(Class<? extends Annotation> annotation, Class<T> annotationTypeTarget) {
        return containsAnnotation(annotation, annotationTypeTarget, Collections.emptySet());
    }

    public static <T extends Annotation> Map<Class<? extends Annotation>, ? extends Annotation> getAnnotations(Method method, Class<T> annotationTypeTarget) {
        return Arrays.stream(method.getAnnotations()).filter(a -> containsAnnotation(a.annotationType(), annotationTypeTarget)).collect(Collectors.toMap(a -> a.annotationType(), Function.identity()));
    }
}

If you have:

@Retention(RetentionPolicy.RUNTIME)
@interface Action {
}

@Action
@Retention(RetentionPolicy.RUNTIME)
@interface SpecificAction {
}

@Action
@Retention(RetentionPolicy.RUNTIME)
@interface ParticularAction {
}

public class Foo{
    @SpecificAction
    @ParticularAction
    public void specificMethod() {
         // ...
    }
}

You can use like this: AnnotationUtil.getAnnotations(specificMethod, Action.class); And this'll return a Map: {interface foo.ParticularAction=@foo.ParticularAction(), interface foo.SpecificAction=@foo.SpecificAction()}

David Pérez Cabrera
  • 4,960
  • 2
  • 23
  • 37
  • The idea indeed crossed my mind, I was looking for a way to avoid doing that and not having to write the recursion. But as it was said in the link, it is good to know that there no such inheritance! – ldebroux Jul 31 '17 at 05:41