4

I have a class that defines a bunch of things, i.e., target names and arrays of parameters:

public class Actions {

    protected static final String A_TARGET = "do_a";
    protected static final String[] A_ARGS = { "paramA", "paramB" };

    ...
}

I am trying to write a custom annotation to avoid action registration in a constructor, so I have defined:

public @interface MyAnno{

    String actionTarget();
    String[] actionArgs();
}

And am trying to use it as such:

@MyAnno(
    actionTarget = Actions.A_TARGET,
    actionArgs = Actions.A_ARGS   <-- compile error
)
public void doA() {
}

I am however experiencing an issue with the marked line, an issue with passing in the String array of arguments to the annotation:

The value for annotation attribute MyAnno.A_ARGS must be an array initializer

If I replace Actions.A_ARGS with the array I have defined in my Actions class, { "paramA", "paramB" }, the error goes away...

My question is:

How can I go about having this args array defined elsewhere, and then using it in the Annotation?

Matt Clark
  • 27,671
  • 19
  • 68
  • 123
  • This `protected static final String A_ARGS = { "paramA", "paramB" };` is not valid Java and this `actionArgs = Actions.A_ARGS` won't work, because `A_ARGS` is not a constant. Looks like you need to duplicate the content of `A_ARGS` in the annotation. – Tom Oct 08 '16 at 20:38
  • `is not valid java`? What is that supposed to mean? This is a pre-existing class that is actively used all over the project. How is it also not a constant? It is a `static final`? – Matt Clark Oct 08 '16 at 20:39
  • He probably meant that it should be String[] A_ARGS = { "paramA", "paramB" } (so String[] and not String), but I'm sure it's just a typo and not relevant here. But he is right that Java won't let you put anything except an array initializer there directly... it's just annoying like that. – kaqqao Oct 08 '16 at 20:42
  • oops :/ yeah.. that was a typo... so, there is no way of defining this array elsewhere? I am trying to avoid having to duplicate it, to keep consistency.. – Matt Clark Oct 08 '16 at 20:43
  • elsewhere? file, database, network? – Roman C Oct 08 '16 at 20:50
  • elsewhere; as in another class, as specified in the question. – Matt Clark Oct 08 '16 at 20:51
  • you can use another class but it should be superclass – Roman C Oct 08 '16 at 20:53
  • I have tried moving the `String[] A_ARGS` definition to even the same class, with no avail. Same error. – Matt Clark Oct 08 '16 at 20:55
  • *"What is that supposed to mean?"* That you can't initialize a String with an array. *"This is a pre-existing class that is actively used all over the project."* Then why don't you use it correctly? *"How is it also not a constant?"* Search for "jls" and "compile time constant". An array isn't one. – Tom Oct 08 '16 at 22:04
  • Can the downvoter please explain why this was downvote worthy? It may be a duplicate, but it was far from a _bad question_. – Matt Clark Oct 08 '16 at 22:21

2 Answers2

3

This is not doable in Java.

See this SO question for details: How to supply value to an annotation from a Constant java

The best you can get is to define the individual array items as constants and reuse them, i.e.

public class Actions {

    protected static final String A_TARGET = "do_a";

    protected static final String PARAM_A = "paramA";
    protected static final String PARAM_B = "paramB";
    protected static final String[] A_ARGS = { PARAM_A, PARAM_B };

    ...
}

And then

@MyAnno(
    actionTarget = Actions.A_TARGET,
    actionArgs = {Actions.PARAM_A, Actions.PARAM_B}
)
public void doA() {
}

This will help to avoid "string duplication" and typo errors, but won't resolve the duplication of the combinations.

Community
  • 1
  • 1
Alex Shesterov
  • 26,085
  • 12
  • 82
  • 103
  • Not quite what I was looking for, but this is definitely a clean solution that I can make work. Much appreciated, Thanks! – Matt Clark Oct 08 '16 at 22:23
1

You cannot use static array field value as a value for annotation, because it is not a constant expression.

Consider following example:

@Target(ElementType.METHOD)
public @interface MyAnno {
    String[] arrayParam();
    String stringParam();
}

public static final String[] arrayConstant = {"x", "y"};
public static final String stringConstant = "z";

static {
    stringConstant = "t"; // error, final string cannot be changed
    arrayConstant[0] = "t"; // valid (thus array is not constant)
}

@MyAnno(
        arrayParam = arrayConstant, // error, array is not a constant
        stringParam = stringConstant // valid, static final string field is a constant
) 
void myMethod() {

}

Static final String fields are proper constant expressions as they cannot be changed in runtime, so compiler marks them as constant.

Static final array fields always can be changed in runtime, so compliler cannot mark it as a constant.

Kostiantyn
  • 935
  • 8
  • 13