3

I have a custom annotation with a single variable.

I use it to annotate attributes in a class and what i need is that the annotation default value for the variable, be the type of the attribute declared. Here the example:

Annotation:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Annotation{
    Class<?> className() default ???????; // <- here i need to set something that tells my annotation to take the class of the attribute annotated
}

Class using Annotation:

public class Main {

    @Annotation
    private AnotherClass annotatedAttribute;

    //other code
}

And so what i need is that when i get the annotatedAttribute field and i get its annotation and its value of the className() variable, the default value should be the equivalent to AnotherClass.class unless i state otherwise in the declaration of the @Annotation

E.g:

@Annotation(classname= YetAnotherClass.class)

Is there a way to do this?

I saw some posts talking about an annotation processor, but in my case i don't want to generate new classes files since my class already exist and i'm fetching the field and the annotation through reflection (so i'm at runtime level)

1 Answers1

4

There is no way to specify a custom logic in an annotation, so you have to leave it to the code processing the annotation at runtime, however, you can’t use null as a marker value either.

The only way to tell your annotation processing tool that custom processing is required, is by choosing a dedicated marker type as default value. This might be a type that would otherwise never occur as a regular annotation value, e.g. void.class, or you create a class solely for serving as the marker.

To show a similar real life example, JUnit’s @Test annotation has an expected element denoting an expected type to be thrown. The default, supposed to express that no exception is expected, can’t be null nor a type outside the Throwable hierarchy as the value must conform to the declared type Class<? extends Throwable>. Therefore, the default value is a dedicated type Test.None that is never thrown and treated specially by the framework when processing the annotation.

For your case, you have to decide for a suitable marker type or create a dedicated type and adapt the processing code to check for the type. E.g.

public final class UseFieldType {
    private UseFieldType() {}
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YourAnnotation {
    Class<?> className() default UseFieldType.class;
}
class YourCodeUsedAtRuntime {
    public static Optional<Class<?>> getAnnotationValue(Field f) {
       YourAnnotation a = f.getAnnotation(YourAnnotation.class);
       if(a == null) return Optional.empty();

       Class<?> type = a.className();
       if(type == UseFieldType.class) type = f.getType();
       return Optional.of(type);
    }
}
class Example {
    @YourAnnotation String string;
    @YourAnnotation(className = Pattern.class) String regEx;
    String none;

    public static void main(String[] args) {
        for(Field f: Example.class.getDeclaredFields()) {
            System.out.println(f.getName() + ": "
                + YourCodeUsedAtRuntime.getAnnotationValue(f)
                      .map(Class::getName).orElse("No annotation"));
        }
    }
}
string: java.lang.String
regEx: java.util.regex.Pattern
none: No annotation
Holger
  • 285,553
  • 42
  • 434
  • 765