How to specify Enum value for annotation from constant in Java

I can not use Enum taken from a constant as a parameter in the annotation. I get this compilation error: "The value of the [attribute] annotation attribute must be a constant expression enum."

This is a simplified version of the code for Enum:

public enum MyEnum { APPLE, ORANGE } 

For annotation:

 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString(); int theInt(); MyEnum theEnum(); } 

And the class:

 public class Sample { public static final String STRING_CONSTANT = "hello"; public static final int INT_CONSTANT = 1; public static final MyEnum MYENUM_CONSTANT = MyEnum.APPLE; @MyAnnotation(theEnum = MyEnum.APPLE, theInt = 1, theString = "hello") public void methodA() { } @MyAnnotation(theEnum = MYENUM_CONSTANT, theInt = INT_CONSTANT, theString = STRING_CONSTANT) public void methodB() { } } 

The error appears only in "theEnum = MYENUM_CONSTANT" by method B. String and int constants are in order with the compiler, the constant Enum is not, although this is the same value as the overA method. It seems to me that this is a missing feature in the compiler, because all three are obviously constants. No method calls, weird use of classes, etc.

What I want to achieve:

  • To use MYENUM_CONSTANT both in the annotation and later in the code.
  • To keep the type of security.

Any way to achieve these goals will be fine.

Edit:

Thanks to everyone. As you say, this is not possible. JLS needs to be updated. This time I decided to forget about the listings in the annotation and use regular int constants. As long as int is assigned from a named constant, values โ€‹โ€‹are limited and type-safe.

It looks like this:

 public interface MyEnumSimulation { public static final int APPLE = 0; public static final int ORANGE = 1; } ... public static final int MYENUMSIMUL_CONSTANT = MyEnumSimulation.APPLE; ... @MyAnnotation(theEnumSimulation = MYENUMSIMUL_CONSTANT, theInt = INT_CONSTANT, theString = STRING_CONSTANT) public void methodB() { ... 

And I can use MYENUMSIMUL_CONSTANT somewhere else in the code.

+58
java enums annotations
Nov 06
source share
6 answers

This seems to be defined in JLS # 9.7.1 :

[...] Type V is assignment compatible (ยง5.2) with T, and furthermore:

  • [...]
  • If T is an enumeration type and V is an enumeration constant.

And the enumeration constant is defined as the actual enumeration constant ( JLS # 8.9.1 ), and not the variable that points to this constant.

Bottom line: if you want to use the enumeration as a parameter for annotation, you will need to give it an explicit value MyEnum.XXXX . If you want to use a variable, you will need to select a different type (not enum).

One possible workaround is to use String or int , which can then be mapped to your enum - you will lose type safety, but errors can be easily detected at runtime (= during tests).

+20
Nov 06
source share

"All problems in computer science can be solved by a different level of indirection" --- David Wheeler

There he is:

Enum class:

 public enum Gender { MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE); Gender(String genderString) { } public static class Constants { public static final String MALE_VALUE = "MALE"; public static final String FEMALE_VALUE = "FEMALE"; } } 

Character class:

 import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import static com.fasterxml.jackson.annotation.JsonTypeInfo.As; import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id; @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = Person.GENDER) @JsonSubTypes({ @JsonSubTypes.Type(value = Woman.class, name = Gender.Constants.FEMALE_VALUE), @JsonSubTypes.Type(value = Man.class, name = Gender.Constants.MALE_VALUE) }) public abstract class Person { ... } 
+81
May 05 '13 at 12:04
source share

I think that the answer with the most votes is incomplete, since it does not guarantee at all that the value of the enumeration is related to the underlying constant value of String . With this solution, you just need to separate the two classes.

Instead, I rather suggest reinforcing the relationship shown in this answer by establishing a correlation between the enumeration name and the constant value as follows:

 public enum Gender { MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE); Gender(String genderString) { if(!genderString.equals(this.name())) throw new IllegalArgumentException(); } public static class Constants { public static final String MALE_VALUE = "MALE"; public static final String FEMALE_VALUE = "FEMALE"; } } 

As @GhostCat noted in a comment, proper unit tests must be done to ensure connectivity.

+17
Feb 03 '17 at 17:37
source share

The control rule looks like this: โ€œIf T is an enumeration type, and V is an enumeration constantโ€, 9.7.1. Normal annotations . It can be seen from the text that JLS strives for an extremely simple evaluation of expressions in annotations. An enumeration constant is, in particular, an identifier used within an enumeration declaration.

Even in other contexts, final initialization with the enum constant is not a constant expression. 4.12.4. final Variables says: "A variable of a primitive type or a String type that is final and initialized by the expression of a compile-time constant (ยง15.28) is called a constant variable." but does not include the end of an enumeration; the type is initialized by an enumeration constant.

I also checked a simple case in which it matters whether the expression is a constant expression - a if the environment is assigned to an unassigned variable. The variable was not assigned. An alternative version of the same code that tested the final int made a specific variable:

  public class Bad { public static final MyEnum x = MyEnum.AAA; public static final int z = 3; public static void main(String[] args) { int y; if(x == MyEnum.AAA) { y = 3; } // if(z == 3) { // y = 3; // } System.out.println(y); } enum MyEnum { AAA, BBB, CCC } } 
+7
Nov 06
source share

I quote from the last line in the question

Any way to achieve these goals will be fine.

So i tried this

  • Added enumType parameter for annotation as placeholder

     @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString(); int theInt(); MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE; int theEnumType() default 1; } 
  • Added getType method in implementation

     public enum MyAnnotationEnum { APPLE(1), ORANGE(2); public final int type; private MyAnnotationEnum(int type) { this.type = type; } public final int getType() { return type; } public static MyAnnotationEnum getType(int type) { if (type == APPLE.getType()) { return APPLE; } else if (type == ORANGE.getType()) { return ORANGE; } return APPLE; } } 
  • Change made to use int constant instead of enumeration

     public class MySample { public static final String STRING_CONSTANT = "hello"; public static final int INT_CONSTANT = 1; public static final int MYENUM_TYPE = 1;//MyAnnotationEnum.APPLE.type; public static final MyAnnotationEnum MYENUM_CONSTANT = MyAnnotationEnum.getType(MYENUM_TYPE); @MyAnnotation(theEnum = MyAnnotationEnum.APPLE, theInt = 1, theString = "hello") public void methodA() { } @MyAnnotation(theEnumType = MYENUM_TYPE, theInt = INT_CONSTANT, theString = STRING_CONSTANT) public void methodB() { } } 

I get the MYENUM constant from MYENUM_TYPE int, so if you change MYENUM, you just need to change the int value to the corresponding value of the enum type.

This is not the most elegant solution, but I give it because of the last line in the question.

Any way to achieve these goals will be fine.

Just pay attention if you try to use

 public static final int MYENUM_TYPE = MyAnnotationEnum.APPLE.type; 

The compiler says in the annotation MyAnnotation.theEnumType must be a constant

+2
Nov 08 '12 at 11:35
source share

My decision was

 public enum MyEnum { FOO, BAR; // element value must be a constant expression // so we needs this hack in order to use enums as // annotation values public static final String _FOO = FOO.name(); public static final String _BAR = BAR.name(); } 

I thought it was the cleanest way. This meets several requirements:

  • If you want enumerations to be numeric
  • If you want the enumerations to be of a different type
  • The compiler notifies you if the refactorin refers to a different value
  • @Annotation(foo = MyEnum._FOO) usage (minus one character): @Annotation(foo = MyEnum._FOO)

EDIT This sometimes leads to a compilation error, which leads to the fact that the original element value must be a constant expression

So this is apparently not an option!

0
Jun 06 '19 at 14:28
source share



All Articles