88

定数から取得した列挙型をアノテーションのパラメーターとして使用できません。このコンパイルエラーが発生します:「アノテーション属性[属性]の値は列挙型定数式でなければなりません」。

これは、列挙型のコードの簡略化されたバージョンです。

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の値とまったく同じ値であっても問題ありません。3つすべてが明らかに定数であるため、これはコンパイラーに欠けている機能であるように私には見えます。メソッド呼び出しやクラスの奇妙な使用などはありません。

私が達成したいのは:

  • 注釈とコードの後半の両方でMYENUM_CONSTANTを使用します。
  • タイプを安全に保つため。

これらの目標を達成するための任意の方法で問題ありません。

編集:

皆さんありがとう。あなたが言うように、それはできません。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_CONSTANTを使用できます。

4

6 に答える 6

139

「コンピュータサイエンスのすべての問題は、別のレベルの間接参照によって解決できます」--- 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 {
...
}
于 2013-05-05T12:04:46.347 に答える
45

列挙値が基礎となる定数値と結合していることをまったく保証しないため、最も投票された回答は不完全だと思います。そのソリューションでは、2つのクラスを分離する必要があります。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が指摘しているように、結合を確認するには、適切な単体テストを実施する必要があります。

于 2017-02-03T17:37:57.047 に答える
25

JLS#9.7.1で定義されているようです:

[...] VのタイプはTと割り当て互換(§5.2)であり、さらに:

  • [...]
  • Tが列挙型で、Vが列挙定数の場合。

また、列挙型定数は、実際の列挙型定数(JLS#8.9.1)として定義され、その定数を指す変数ではありません。

結論:注釈のパラメーターとして列挙型を使用する場合は、明示的な値を指定する必要がありMyEnum.XXXXます。変数を使用する場合は、別の型(列挙型ではない)を選択する必要があります。

String考えられる回避策の1つは、またはを使用intして列挙型にマップできるようにすることです。型の安全性は失われますが、実行時(=テスト中)にエラーを簡単に見つけることができます。

于 2012-11-06T15:11:07.613 に答える
7

制御規則は、「Tが列挙型で、Vが列挙定数の場合」のようです。9.7.1。通常の注釈。本文から、JLSは注釈の表現の非常に簡単な評価を目指しているようです。列挙型定数は、具体的には列挙型宣言内で使用される識別子です。

他のコンテキストでも、列挙型定数で初期化されたfinalは定数式ではないようです。4.12.4。final Variablesは、「プリミティブ型またはタイプStringの変数は、finalであり、コンパイル時の定数式(§15.28)で初期化され、定数変数と呼ばれます。」と述べていますが、列挙型のfinalは含まれていません。列挙型定数。

また、式が定数式であるかどうかが重要である単純なケースをテストしました。つまり、割り当てられていない変数への割り当てを囲む場合です。変数は割り当てられませんでした。最終的な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
    }
  }
于 2012-11-06T15:10:04.283 に答える
3

質問の最後の行から引用します

これらの目標を達成するための任意の方法で問題ありません。

だから私はこれを試しました

  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定数はMYENUM_TYPEintから導出するため、MYENUMを変更する場合は、int値を対応する列挙型の値に変更する必要があります。

それは最もエレガントな解決策ではありませんが、質問の最後の行のために私はそれを与えています。

これらの目標を達成するための任意の方法で問題ありません。

使用してみた場合の補足

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

コンパイラは注釈で言います-MyAnnotation.theEnumTypeは定数でなければなりません

于 2012-11-08T11:35:25.053 に答える
-1

私の解決策は

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

これが一番きれいな方法だと思いました。これは、いくつかの要件を満たしています。

  • 列挙型を数値にしたい場合
  • 列挙型を他のタイプにしたい場合
  • リファクタリングが別の値を参照している場合、コンパイラは通知します
  • 最もクリーンなユースケース(マイナス1文字):@Annotation(foo = MyEnum._FOO)

編集

これにより、コンパイルエラーが発生することがあり、これが元の理由につながります。element value must be a constant expression

したがって、これは明らかにオプションではありません!

于 2019-06-06T14:28:11.120 に答える