Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中从常量向注释提供枚举值_Java_Enums_Annotations - Fatal编程技术网

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

如何在Java中从常量向注释提供枚举值,java,enums,annotations,Java,Enums,Annotations,我无法将从常量中获取的枚举用作批注中的参数。我得到这个编译错误:“注释属性[attribute]的值必须是枚举常量表达式” 这是枚举代码的简化版本: public enum MyEnum { APPLE, ORANGE } 对于注释: @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString();

我无法将从常量中获取的枚举用作批注中的参数。我得到这个编译错误:“注释属性[attribute]的值必须是枚举常量表达式”

这是枚举代码的简化版本:

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_常量
  • 保持类型安全
任何实现这些目标的方法都可以

编辑:

谢谢大家。正如你所说,这是做不到的。应更新JLS。这次我决定忘记注释中的枚举,使用常规int常量。只要int是从一个命名常量赋值的,那么这些值是有界的,并且是“排序”类型安全的

看起来是这样的:

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
,然后映射到您的枚举-您将失去类型安全性,但在运行时(=测试期间)很容易发现错误。

我引用问题最后一行的话

任何实现这些目标的方法都可以

所以我试过这个

  • 将enumType参数作为占位符添加到批注中

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface MyAnnotation {
    
        String theString();
        int theInt();
        MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE;
        int theEnumType() default 1;
    }
    
  • 在实现中添加了一个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;
        }
    }
    
  • 更改为使用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值更改为相应的枚举类型值

    这不是最优雅的解决方案,但我给出它是因为问题的最后一行

    任何实现这些目标的方法都可以

    如果你尝试使用

    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