如何从Java中的常量提供枚举值的注释

我无法使用从常量中取得的Enum作为注释中的参数。 我得到这个编译错误:“注释属性[属性]的值必须是一个枚举常量expression式”。

这是Enum代码的简化版本:

public enum MyEnum { APPLE, ORANGE } 

对于注释:

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

和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() { } } 

此错误仅在方法B的“theEnum = MYENUM_CONSTANT”中显示。 string和int常数对编译器来说是可以的,Enum常量不是,尽pipe它和methodA的值完全相同。 在我看来,这是在编译器中缺less的function,因为所有三个显然是常数。 没有方法调用,没有奇怪的类使用等。

我想要达到的是:

  • 在注释和后面的代码中使用MYENUM_CONSTANT。
  • 保持types安全。

任何达到这些目标的方法都可以。

编辑:

谢谢大家。 正如你所说,这是不能做到的。 JLS应该更新。 这次我决定忘记注释中的枚举,并使用常规的int常量。 只要int是从一个命名常量分配的,这些值是有界的,它是“types”types安全的。

它看起来像这样:

 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() { ... 

我可以在代码中的任何其他地方使用MYENUMSIMUL_CONSTANT。

这似乎是在JLS#9.7.1中定义的:

[…] V的types是与T兼容的(§5.2),此外:

  • […]
  • 如果T是枚举types,并且V是枚举常量。

而枚举常量被定义为实际枚举常量( JLS#8.9.1 ),而不是指向该常量的variables。

底线:如果你想使用一个枚举作为你的注释参数,你需要给它一个明确的MyEnum.XXXX值。 如果你想使用一个variables,你将需要select另一种types(而不是一个枚举)。

一个可能的解决方法是使用一个string或int,然后你可以映射到你的枚举 – 你会失去types安全性,但错误可以很容易地在运行时(testing期间)被发现。

“计算机科学中的所有问题都可以通过另一个层面的间接性来解决”— David Wheeler

这里是:

枚举类:

 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"; } } 

人员类别:

 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 { ... } 

控制规则似乎是“如果T是一个枚举types,并且V是一个枚举常量”, 9.7.1。 正常注释 。 从文本看来,JLS的目标是非常简单地评估注释中的expression式。 枚举常量具体是枚举声明中使用的标识符。

即使在其他情况下,使用枚举常量初始化的最终结果似乎也不是一个常量expression式。 4.12.4。 最终variables表示“原始types或types的variables是最终的,并用编译时常量expression式(第15.28)初始化,被称为常量variables。”,但不包括枚举types的最后一个初始化枚举常量。

我还testing了一个简单的例子,在这个例子中,一个expression式是否是一个常量expression式 – 如果包含一个赋值给一个未赋值的variables。 该variables没有被赋值。 testing最终int的相同代码的替代版本确实赋予variables明确的分配:

  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 } } 

我认为最被投票的答案是不完整的,因为它不能保证枚举值与基本的常量String值相结合 。 有了这个解决scheme,人们应该解耦这两个类。

相反,我宁愿build议通过执行枚举名称和常量值之间的关联来加强该答案中显示的耦合,如下所示:

 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"; } } 

另外,应该写unit testing来检查string是否是常量。

我引用问题的最后一行

任何达到这些目标的方法都可以。

所以我试了这个

  1. 添加一个enumType参数作为占位符注释

     @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString(); int theInt(); MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE; int theEnumType() default 1; } 
  2. 在实现中添加了一个getType方法

     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; } } 
  3. 更改使用int常量而不是枚举

     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() { } } 

我从MYENUM_TYPE int派生了MYENUM常量,所以如果你改变了MYENUM,你只需要把int值改成相应的枚举types值即可。

它不是最优雅的解决scheme,但是我正在给它,因为问题的最后一行。

任何达到这些目标的方法都可以。

只是一个侧面说明,如果你尝试使用

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

编译器在注释中说 – MyAnnotation.theEnumType必须是一个常量