Search code examples
javaannotations

Java Repeatable annotation not recognized when it's only one parameter


I want to create an option class that holds a key-value structure.

This Map is filled with the content of a configuration file at runtime.

To validate that Config I want do define required keys per annotation of the Option class like:

// Map must contain a entry with key 'foo' and key 'bar'
@requiredKey("foo")
@requiredKey("bar")
class Options {
   Map<String, String> optionsMap;
}

Therefore I created a repeatable annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface requiredKeys {
    requireKey[] value();
}

@Repeatable(requiredKeys)
public @interface requredKey {
    String value();
}

At runtime I call requiredKey[] anno = options.getAnnotationsByType(requiredKey.class))

This works fine if the number of specified annotations is > 1. But if the number of annotations is exactly one, I cant obtain it (getAnnotationsByType returns an empty array)

working:

@requiredKey("foo")
@requiredKey("bar")
class Options {
   Map<String, String> optionsMap;
}

// anno holds 'foo' and 'bar'
requiredKey[] anno = options.getAnnotationsByType(requiredKey.class))

not working:

@requiredKey("foo")
class Options {
   Map<String, String> optionsMap;
}

// anno is empty
requiredKey[] anno = options.getAnnotationsByType(requiredKey.class))

I don't understand this behaviour :(

So my Questions are:

  • what's the explanation for such an behaviour?
  • how can I get this working?

Thanks


Solution

  • You need to add to your @requiredKey the retention policy:

    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(requiredKeys)
    public @interface requredKey {
        String value();
    }
    

    If you don't, then when you create a class with one annotation, Java does not create a requiredKeys annotation because you have only one Annotation. Therefor, the retention policy of the @requiredKey is applied. In your case, you had none, which means that your annotation won't be visible by the JVM.

    Other comments: Please use Capital letter for your class/annotations.

    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequiredKeys {
        requireKey[] value();
    }
    
    @Repeatable(RequiredKeys)
    public @interface RequiredKey {
        String value();
    }