如何在Java中从常量向注释提供枚举值
我无法将从常量中获取的枚举用作批注中的参数。我得到这个编译错误:“注释属性[attribute]的值必须是枚举常量表达式” 这是枚举代码的简化版本:如何在Java中从常量向注释提供枚举值,java,enums,annotations,Java,Enums,Annotations,我无法将从常量中获取的枚举用作批注中的参数。我得到这个编译错误:“注释属性[attribute]的值必须是枚举常量表达式” 这是枚举代码的简化版本: public enum MyEnum { APPLE, ORANGE } 对于注释: @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString();
public enum MyEnum {
APPLE, ORANGE
}
对于注释:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MyAnnotation {
String theString();
int theInt();
MyEnum theEnum();
}
班级:
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() {
}
}
该错误仅在methodB上的“theEnum=MYENUM_常量”中显示。字符串和int常量在编译器中是可以接受的,枚举常量则不是,即使它的值与methodA上的值完全相同。在我看来,这是编译器中缺少的功能,因为这三个显然都是常量。没有方法调用,没有类的奇怪用法,等等
我想要实现的是:
- 在注释和以后的代码中使用MYENUM_常量
- 保持类型安全
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_常量。控制规则似乎是“如果T是枚举类型,V是枚举常量。”。从文本来看,JLS的目标似乎是对注释中的表达式进行极其简单的评估。枚举常量是枚举声明中使用的标识符 即使在其他上下文中,使用enum常量初始化的final似乎也不是常量表达式。表示“原语类型或字符串类型的变量,即final并用编译时常量表达式(§15.28)初始化的变量,称为常量变量”。但不包括用enum常量初始化的enum类型的final 我还测试了一个简单的例子,在这个例子中,一个表达式是否是常量表达式很重要——一个围绕未赋值变量赋值的if。变量没有被赋值。测试最终整数的同一代码的另一个版本确实明确指定了变量:
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
}
}
它的定义似乎如下: […]V的类型与T的赋值兼容(§5.2),此外:
- [……]
- 如果T是枚举类型,V是枚举常量李>
MyEnum.XXXX
值。如果要使用变量,则需要选择其他类型(而不是枚举)
一种可能的解决方法是使用
字符串
或int
,然后映射到您的枚举-您将失去类型安全性,但在运行时(=测试期间)很容易发现错误。我引用问题最后一行的话
任何实现这些目标的方法都可以
所以我试过这个
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MyAnnotation {
String theString();
int theInt();
MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE;
int theEnumType() default 1;
}
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;
}
}
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() {
}
}
public static final int MYENUM_TYPE = MyAnnotationEnum.APPLE.type;
编译器在注释-MyAnnotation.thenumtype必须是一个常量“计算机科学中的所有问题都可以通过另一个间接层次来解决”——大卫·惠勒
这是:
枚举类:
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 {
...
}
我认为这是不完整的,因为它根本不能保证枚举值与基础常量String
值耦合。有了这个解决方案,我们应该将这两个类解耦
相反,我建议通过强制执行枚举名称和常量值之间的相关性来加强该答案中显示的耦合,如下所示:
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";
}
}
正如在评论中指出的,必须进行适当的单元测试以确保耦合。我的解决方案是
公共枚举MyEnum{
福,
酒吧;
//元素值必须是常量表达式
//因此,我们需要这种方法来使用枚举作为
//注释值
公共静态最终字符串_FOO=FOO.name();
公共静态最终字符串_BAR=BAR.name();
}
我认为这是最干净的方式。这符合两个要求:
- 如果希望枚举为数字
- 如果希望枚举为其他类型
- 如果重构引用了不同的值,编译器会通知您
- 最干净的用例(减去一个字符):
@注释(foo=MyEnum.\u foo)
元素值必须是常量表达式的原因
所以这显然不是一个选择 另请参考此以了解类似的问题。但它并不完全是类型安全的,因为“MYENUM_类型”可以接受非法值(即30),编译器不会注意到。我还认为,不需要额外的代码也可以做到这一点:公共静态final int MYENUM_int_常量=0;公共静态最终MyEnum MyEnum_常量=MyEnum.values()[MyEnum_INT_常量]@MyAnnotation(numsimulation=MYENUM\u INT\u常量,theInt=INT\u常量,theString=STRING\u常量)公共voi