programing

Java의 상수에서 주석에 Enum 값을 제공하는 방법

nasanasas 2020. 11. 17. 08:10
반응형

Java의 상수에서 주석에 Enum 값을 제공하는 방법


상수에서 가져온 Enum을 주석의 매개 변수로 사용할 수 없습니다. 이 컴파일 오류가 발생합니다. "[attribute] 주석 속성 값은 열거 형 상수 표현식이어야합니다."

이것은 Enum에 대한 코드의 단순화 된 버전입니다.

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_CONSTANT"에서만 나타납니다. String 및 int 상수는 컴파일러에서 괜찮습니다. Enum 상수는 methodA와 정확히 동일한 값이지만 그렇지 않습니다. 세 가지 모두 분명히 상수이기 때문에 이것이 컴파일러에서 누락 된 기능 인 것처럼 보입니다. 메서드 호출이나 클래스의 이상한 사용 등이 없습니다.

내가 달성하고 싶은 것은 :

  • 주석과 나중에 코드에서 모두 MYENUM_CONSTANT를 사용합니다.
  • 형식을 안전하게 유지합니다.

이러한 목표를 달성하는 모든 방법은 괜찮습니다.

편집하다:

모두 감사합니다. 당신이 말했듯이 그것은 할 수 없습니다. JLS를 업데이트해야합니다. 이번에는 주석에서 열거 형을 잊어 버리고 일반 정수 상수를 사용하기로 결정했습니다. 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_CONSTANT를 사용할 수 있습니다.


JLS # 9.7.1에 정의 된 것 같습니다 .

[...] V의 유형은 T와 할당 호환 (§5.2)이며 더 나아가 :

  • [...]
  • T가 열거 형이고 V가 열거 형 상수 인 경우.

그리고 열거 형 상수는 해당 상수를 가리키는 변수가 아니라 실제 열거 형 상수 ( JLS # 8.9.1 ) 로 정의됩니다 .

요점 : enum을 주석의 매개 변수로 사용하려면 명시적인 MyEnum.XXXX을 제공해야 합니다. 변수를 사용하려면 열거 형이 아닌 다른 유형을 선택해야합니다.

한 가지 가능한 해결 방법은을 사용하는 것입니다 String또는 int당신이 다음 열거에 매핑 할 수 - 당신이 유형의 안전을 풀어하지만 오류가 런타임에 쉽게 발견 할 수있다 (= 테스트 중).


"컴퓨터 과학의 모든 문제는 다른 수준의 간접적 인 방법으로 해결할 수 있습니다."--- David Wheeler

여기있어:

Enum 클래스 :

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

@GhostCat 이 주석에서 지적했듯이 적절한 단위 테스트가 결합되어 있는지 확인해야합니다.


제어 규칙은 "T가 열거 형이고 V가 열거 형 상수 인 경우", 9.7.1입니다. 일반 주석 . 텍스트에서 JLS는 주석의 표현에 대한 매우 간단한 평가를 목표로하는 것으로 보입니다. 열거 형 상수는 특히 열거 형 선언 내에서 사용되는 식별자입니다.

다른 컨텍스트에서도 enum 상수로 초기화 된 final은 상수 표현식이 아닌 것 같습니다. 4.12.4. final Variables 는 "최종이고 컴파일 타임 상수 식 (§15.28)으로 초기화 된 원시 유형 또는 String 유형의 변수를 상수 변수라고합니다."라고 말하지만, 열거 형 상수.

또한 표현식이 상수 표현식인지 여부가 중요한 간단한 사례를 테스트했습니다. 즉 할당되지 않은 변수에 대한 할당을 둘러싼 경우입니다. 변수가 할당되지 않았습니다. 대신 최종 int를 테스트 한 동일한 코드의 대체 버전은 변수를 확실히 할당했습니다.

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

질문의 마지막 줄에서 인용합니다.

이러한 목표를 달성하는 모든 방법은 괜찮습니다.

그래서 나는 이것을 시도했다

  1. Added a enumType parameter to the annotation as a placeholder

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface MyAnnotation {
    
        String theString();
        int theInt();
        MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE;
        int theEnumType() default 1;
    }
    
  2. Added a getType method in the 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;
        }
    }
    
  3. Made a change to use an int constant instead of the enum

    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 derive the MYENUM constant from MYENUM_TYPE int, so if you change MYENUM you just need to change the int value to the corresponding enum type value.

Its not the most elegant solution, But i'm giving it because of the last line in the question.

Any way to achieve these goals would be fine.

Just a side note, if you try using

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

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


My solution 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 this was the cleanest way. This meets couple of requirements:

  • If you want the enums to be numeric
  • If you want the enums to be of some other type
  • Compiler notifies you if a refactor references a different value
  • Cleanest use-case (minus one character): @Annotation(foo = MyEnum._FOO)

EDIT

This leads occasionally to compilation error, which leads to the reason of the original element value must be a constant expression

So this is apparently not an option!

참고URL : https://stackoverflow.com/questions/13253624/how-to-supply-enum-value-to-an-annotation-from-a-constant-in-java

반응형