5

I tried to create a "map" using nested JAVA annotations.

public @interface EnvInstance {
    Env env();
    Instance instance();
}

public @interface Configuration {
    String description();
    EnvInstance[] envInstances() default {};
}

@Configuration(description = "Test", envInstances = {
    @EnvInstance(env = Env.CERT, instance = Instance.FIRST),
    @EnvInstance(env = Env.INTEGR, instance = Instance.SECOND),
    @EnvInstance(env = Env.PROD, instance = Instance.FIRST),
    ...
}
)
public class TestObject {

}

It seems to work but there is one thing I don't know how to achieve. I want to create two default sets of envInstances configuration so I can type:

@Configuration(description = "Test", envInstances = SET_ONE)
public class TestObject {
}

or

@Configuration(description = "Test", envInstances = SET_TWO)
public class TestObject {
}

Is there a possibility to create a static array of inner annotations or something like this and pass it to the outer annotation?

2 Answers 2

2

I am afraid that there is no way to extract this duplication.

You cannot supply an array value to annotation from constant (read more). It is also not possible to create annotation that extends another annotation (read more).

I don't know the context, but have you considered passing this information into the object itself, and storing it as a field, rather than via annotations?

Another potential solution that might work is to make these classes implement a marker interface and annotate an interface instead. Annotations however are not inherited. If you can modify your parser (or whatever is reading the annotations), you could do something like this:

@Retention(RetentionPolicy.RUNTIME)
public @interface X {
    String[] value();
}

@X({"a", "b", "c"})
interface AnInterface {}

public static class TestClass implements AnInterface {}

public static void main(String[] args) {
    // annotations are not inherited, empty array
    System.out.println(Arrays.toString(TestClass.class.getAnnotations()));

    // check if TestClass is annotated with X and get X.value()
    Arrays.stream(TestClass.class.getAnnotatedInterfaces())
            .filter(type -> type.getType().equals(AnInterface.class))
            .map(type -> (Class<AnInterface>) type.getType())
            .findFirst()
            .ifPresent(anInterface -> {
                String[] value = anInterface.getAnnotation(X.class).value();
                System.out.println(Arrays.toString(value));
            });
}
Sign up to request clarification or add additional context in comments.

4 Comments

Not an option. :( I guess I will have to be forced to copy-paste all the configuration for every single environment for every single object. Anyway I see a nice functionality for incoming Java releases. ;)
I will wait couple hours for other answers. Maybe someone will figure it out. If not, I'll mark your answer as correct. Thanks!
@aerion there is one more possibility but requires to modify the parser (or whatever reads the annotations). See my edit.
Clever but not very elegant and makes hard to override other configuration data. Anyway thanks!
2

Is there a possibility to create a static array of inner annotations or something like this and pass it to the outer annotation?

No. For primitive or string-valued annotation elements you can declare a static final constant somewhere else and refer to that in the annotation, but this does not work for array-valued elements.

The Java language specification section 9.7.1 mandates that any value specified for an array-valued annotation element that does not syntactically start with an opening brace must be treated as shorthand for a single-element array, i.e. the parser treats

@Configuration(description = "Dupa", envInstances = SET_ONE)

as if it said

@Configuration(description = "Dupa", envInstances = {SET_ONE})

and fails because you're trying to set envInstances to an EnvInstance[][] instead of an EnvInstance[].


The precise wording (my bold on the sections that apply to this case):

It is a compile-time error if the element type is not commensurate with the element value. An element type T is commensurate with an element value V if and only if one of the following is true:

  • T is an array type E[], and either:
    • If V is a ConditionalExpression or an Annotation, then V is commensurate with E; or
    • If V is an ElementValueArrayInitializer, then each element value that V contains is commensurate with E.

[snip]

  • T is not an array type, and the type of V is assignment compatible (§5.2) with T, and:
    • If T is a primitive type or String, then V is a constant expression (§15.28).

[snip]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.