Search code examples
javagenericsannotationsjax-rsannotation-processing

How to execute method with same name from different annotations


Even though my question origins from annotation processing, my question is more Java annotation related.

I was writing some code until I realised I didn't know a good way to implement something.

The program uses annotation-processing, I'm trying to get the value of multiple JAX-RS annotations, let's take @PathParam and @QueryParam as example. Both annotations have the abstract method called value()


The following piece of code is an example of how I do not want to write it. I'd have to do this for each JAX-RS annotation.

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

    for(Element element : roundEnv.getElementsAnnotatedWith(PathParam.class)) {
        PathParam parameter = element.getAnnotation(PathParam.class);
        String value = parameter.value();

        // Process data & more program code.
    }

    for(Element element : roundEnv.getElementsAnnotatedWith(QueryParam.class)) {
        QueryParam parameter = element.getAnnotation(QueryParam.class);
        String value = parameter.value();

        // Process data & more program code.
    }

    // Etc... do the same for other JAX-RS annotations.

    return true;
}

I know with abstract classes you can do the following:

abstract class Animal { 
    abstract String name();
}

class Dog extends Animal {
    public String name() {
        return "Dog";
    }
}

class Cat extends Animal {
    public String name() {
        return "Cat";
    }
}

Animal animal = new Cat();
System.out.println(animal.name()); // Prints 'Cat'

animal = new Dog();
System.out.println(animal.name()); // Prints 'Dog'

But I'm not sure how to accomplish a similar thing using annotation since there is no superclass which it can be cast to. I'm imagining it should be something along the lines of this:

ArrayList<Class<? extends Annotation>> annotationsToCheck = 
        new ArrayList<>(Arrays.asList(PathParam.class, QueryParam.class));

for(Class<? extends Annotation> annotationToCheck : annotationsToCheck) {
    for(Element element : roundEnv.getElementsAnnotatedWith(annotationToCheck)) {

        // Somehow cast it to something so that the value() method can be accessed

        // Process data & more program code.

    }

}

I feel like I'm close but I just can't quite put my finger on it. Is there a good way to solve my problem?


Solution

  • In Java 9+, no casting is needed:

    for (Element element : roundEnv.getElementsAnnotatedWithAny​(Set.of(PathParam.class,
                                                                       QueryParam.class))) {
        PathParam pathParam = element.getAnnotation(PathParam.class);
        if (pathParam != null) {
            String value = pathParam.value();
            // Process @PathParam value
        }
        QueryParam queryParam = element.getAnnotation(QueryParam.class);
        if (queryParam != null) {
            String value = queryParam.value();
            // Process @QueryParam value
        }
    }
    

    Or, if you only expect one of them:

    for (Element element : roundEnv.getElementsAnnotatedWithAny​(Set.of(PathParam.class,
                                                                       QueryParam.class))) {
        String value = null;
        PathParam pathParam = null;
        QueryParam queryParam = null;
        if ((pathParam = element.getAnnotation(PathParam.class)) != null) {
            value = pathParam.value();
        } else if ((queryParam = element.getAnnotation(QueryParam.class)) != null) {
            value = queryParam.value();
        }
        // Process value. Exactly one of pathParam and queryParam will be non-null
    }