Java switchステートメント:定数式が必要ですが、定数です


174

だから、私はいくつかの静的定数を持つこのクラスに取り組んでいます:

public abstract class Foo {
    ...
    public static final int BAR;
    public static final int BAZ;
    public static final int BAM;
    ...
}

次に、定数に基づいて関連する文字列を取得する方法を教えてください。

public static String lookup(int constant) {
    switch (constant) {
        case Foo.BAR: return "bar";
        case Foo.BAZ: return "baz";
        case Foo.BAM: return "bam";
        default: return "unknown";
    }
}

しかし、コンパイルするconstant expression requiredと、3つのケースラベルのそれぞれでエラーが発生します。

コンパイラは、スイッチをコンパイルするために、コンパイル時に式がわかっている必要があることを理解していますが、なぜFoo.BA_定数ではないのですか?


1
この場合に列挙型を使用しない理由はありますか?
barrowc '30 / 09/30

1
Javaに列挙型があるとは思いませんでした。public static final intsはJDK全体に散らばっているので、それが私が行ったことです。
オースティンハイド


4
そして、Effective Java(java.sun.com/docs/books/effective)、アイテム30:int定数の代わりに列挙型を使用する
Sean Patrick Floyd

ヒントの人たちに感謝します、私はそれらをチェックします。
オースティンハイド

回答:


150

コンパイラは、スイッチをコンパイルするために、コンパイル時に式がわかっている必要があることを理解していますが、なぜFoo.BA_定数でないのですか?

これらは、フィールドが初期化された後に実行されるコードの観点からは一定ですが、JLSで必要な意味でのコンパイル時定数ではありません。定数式1の仕様については、§15.28定数式を参照してください。これは、次のように「定数変数」を定義する4.12.4最終変数を参照します。

最終的なものであり、コンパイル時の定数式(15.28)で初期化されるプリミティブ型またはString型の変数を定数変数と呼びます。変数が定数変数であるかどうかは、クラスの初期化(§12.4.1)、バイナリ互換性(§13.1、§13.4.9)、および明確な代入(§16)に影響を与える可能性があります。

あなたの例では、Foo.BA *変数には初期化子がないため、「定数変数」としての資格はありません。修正は簡単です。Foo.BA *変数宣言を変更して、コンパイル時の定数式である初期化子を設定します。

他の例(初期化子がすでにコンパイル時の定数式である)では、final必要に応じて変数を宣言します。

定数でenumはなくコードを使用するようにコードを変更することもできますintが、これには別の制限がいくつかあります。


1-定数式の制限は次のように要約できます。定数式)プリミティブ型と使用することができるString)のみ、Bをリテラル(別にある原色許可nullのみ)と定数、変数、c)は、おそらく部分式、Dのように括弧の定数式を許す)、代入演算子を除く演算子を許可++--またはinstanceof、およびe)プリミティブ型への型キャストを許可しStringます。

これは、メソッドまたはラムダの呼び出しのいずれかの形式を含んでいないことに注意してください、new.class.lengthまたは配列の添え字。さらに、配列値、enum値、プリミティブラッパータイプの値、ボックス化およびボックス化解除の使用は、a)のためにすべて除外されます。


79

あなたは取得に必要な定数式、あなたの定数オフ値を残しているために。試してください:

public abstract class Foo {
    ...
    public static final int BAR=0;
    public static final int BAZ=1;
    public static final int BAM=2;
    ...
}

48

私はAndroidでこのエラーを受け取りました、そして私の解決策はただ使うことでした:

public static final int TAKE_PICTURE = 1;

の代わりに

public static int TAKE_PICTURE = 1;

3
明確にするために:これは、静的プロパティをfinalにすることでエラーを解決します。私の元の質問では、問題は、最終的な静的プロパティに初期化子が欠落していて、それが定数になるが、コンパイル時の定数ではないということでした。詳細については、承認された回答を参照してください。
オースティンハイド

4
私はそれが別の問題であることを知っていますが、私と一緒にここに来たので、同じ状況で他の誰かを助けることができました。
Teo Inke 2015

これらの値がランタイムを変更する可能性がある場合に問題が発生するため、これらは最終的なものでなければならないことは理にかなっています。
スロット、2015

31

それらはコンパイル時定数ではないからです。次の有効なコードを検討してください。

public static final int BAR = new Random().nextInt();

BAR実行時にのみの値を知ることができます。


1
面白い。うまくいくでしょうpublic static final int BAR = new Random().nextInt()か?
ティロ

4
Thiloのステートメントはコンパイルされますが、switchステートメントは定数式が必要であることを訴えます。さらに、2つの連続したnew Random().nextInt()値が同じ値を返すことはできませんか?
Tony Ennis

2
@トニー:それは良いことです。コンパイル時定数で初期化されていないため、コンパイルされません。スティーブンの受け入れられた答えを見てください。それがコンパイルされた場合、ランダムな整数がクラスにハードコードされ、非常に予測できない結果になります。
ティロ

スイッチの定数が拒否され、「定数」自体が拒否されないことに驚いています。それがこのようになるとは思いもしませんでした。もちろん、それは本当に私が思う定数ではありません。
Tony Ennis

@TonyEnnis-それはあなたが本当に一定で何を意味するかによって異なります。これは、プログラムの実行中に変更されないという意味で、真に一定です(2、3の小節を法として)。しかし、それはすべての処刑について同じではありません。
スティーブンC

17

次の例のように列挙型を使用できます。

public class MainClass {
enum Choice { Choice1, Choice2, Choice3 }
public static void main(String[] args) {
Choice ch = Choice.Choice1;

switch(ch) {
  case Choice1:
    System.out.println("Choice1 selected");
    break;
 case Choice2:
   System.out.println("Choice2 selected");
   break;
 case Choice3:
   System.out.println("Choice3 selected");
   break;
    }
  }
}

ソース: 列挙型のSwitchステートメント


こんにちは、私はまだこのように列挙型を使用する問題を抱えています:<br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/>スイッチで列挙型を使用しようとすると、同じエラーが発生します... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/>問題を解決することは可能ですか?
少林寺2015年

1
@stiga-enumインスタンス自体のみをオンに切り替えることができます。enumインスタンスでメソッドを呼び出すことによって返される一部の値ではありません。
Stephen C

3

これは何年も前に回答されたもので、おそらく関係ありませんが、念のためです。この問題に直面したとき、のif代わりに単にステートメントを使用しただけでswitch、エラーが解決しました。もちろんこれは回避策であり、おそらく「正しい」解決策ではありませんが、私の場合はそれで十分でした。


4
これは、回避策ではなく、質問への答えである
J. Doeの

なぜ私はここで票を落とし続けるのですか?それは正当な回避策です
Samer Murad '20

2
おそらく、スイッチを使用して回避しようとしているのはIFステートメントのためです
Dean Wild

1
ここでの質問は問題を解決する「方法」ではなく、問題が発生した「理由」であるため、私は反対票を投じました。あなたの答えは文脈から外れていると思います。あなたのしている完璧主義場合にも、あなたはそれを実現する必要がありswitch、一般的に速く、長いよりもif-elseので、switch唯一の状態をチェックした後でいる間、if-elseあなたは確認する必要があり、すべての正しいものを見つける前に条件を。
Christian Lim

0

たとえば、スイッチ変数がそのエラーを発生させることもあります。

switch(view.getTag()) {//which is an Object type

   case 0://will give compiler error that says Constant expression required

   //...
}

解決するには、変数をintにキャストする必要があります(この場合)。そう:

switch((int)view.getTag()) {//will be int

   case 0: //No Error

   //...
}

0

このようなことをしているときにAndroidでこのエラーが発生しました:

 roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            switch (parent.getItemAtPosition(position)) {
                case ADMIN_CONSTANT: //Threw the error

            }

定数を宣言しているにもかかわらず:

public static final String ADMIN_CONSTANT= "Admin";

コードを次のように変更して問題を解決しました:

roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String selectedItem = String.valueOf(parent.getItemAtPosition(position));
            switch (selectedItem) {
                case ADMIN_CONSTANT:

            }

0

私の場合、私はこの例外を受けていました

switch (tipoWebServ) {
                            case VariablesKmDialog.OBTENER_KM:
                                resultObtenerKm(result);
                                break;
                            case var.MODIFICAR_KM:
                                resultModificarKm(result);
                                break;
                        }

2番目のケースでは、インスタンスから定数を呼び出してvar.MODIFICAR_KM:いましたがVariablesKmDialog.OBTENER_KM、クラスから直接使用する必要があります。


0

スイッチケースで使用する場合は、その値をスイッチに接続する前でも、列挙型を取得する必要があります。例えば ​​:

SomeEnum someEnum = SomeEnum.values()[1];

switch (someEnum) {
            case GRAPES:
            case BANANA: ...

そして列挙型は次のようなものです:

public enum SomeEnum {

    GRAPES("Grapes", 0),
    BANANA("Banana", 1),

    private String typeName;
    private int typeId;

    SomeEnum(String typeName, int typeId){
        this.typeName = typeName;
        this.typeId = typeId;
    }
}

0

以下のコードは自明ですが、スイッチケースで列挙型を使用できます。

/**
 *
 */
enum ClassNames {
    STRING(String.class, String.class.getSimpleName()),
    BOOLEAN(Boolean.class, Boolean.class.getSimpleName()),
    INTEGER(Integer.class, Integer.class.getSimpleName()),
    LONG(Long.class, Long.class.getSimpleName());
    private Class typeName;
    private String simpleName;
    ClassNames(Class typeName, String simpleName){
        this.typeName = typeName;
        this.simpleName = simpleName;
    }
}

enumからのクラス値に基づいて、マッピングできます。

 switch (ClassNames.valueOf(clazz.getSimpleName())) {
        case STRING:
            String castValue = (String) keyValue;
            break;
        case BOOLEAN:
            break;
        case Integer:
            break;
        case LONG:
            break;
        default:
            isValid = false;

    }

それが役に立てば幸い :)


0

次の方法を使用することをお勧めします。

public enum Animal {
    DOG("dog"), TIGER("tiger"), LION("lion");
    private final String name;

    @Override
    public String toString() {
        return this.name;
    }
}


public class DemoSwitchUsage {

     private String getAnimal(String name) {
         Animal animalName = Animal.valueOf(name);
         switch(animalName) {
         case DOG:
             // write the code required.
             break;
         case LION:
             // Write the code required.
             break;
         default:
             break;
         }
     }
}

:私は、列挙型は、次のコンストラクタ持つべきだと思う private Animal(String name) { this.name = name; }
user1364368を

-1

列挙型を使用することをお勧めします:)

これをチェックしてください:

public enum Foo 
{
    BAR("bar"),
    BAZ("baz"),
    BAM("bam");

    private final String description;

    private Foo(String description)
    {
        this.description = description;
    }

    public String getDescription()
    {
        return description;
    }
}

その後、次のように使用できます。

System.out.println(Foo.BAR.getDescription());

@djangofanコードを実行しているJDKのバージョンは何ですか?
エバートン

IntelliJ-IDEA 14でJDK 1.7.0_74を使用しました
djangofan

1
私はEverton Agnerによって提案されたのと同じクラスを使用していますが、必要な定数式を示しています。
2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.