Pernyataan beralih Java: Diperlukan ekspresi konstan, tetapi IS konstan


175

Jadi, saya sedang mengerjakan kelas ini yang memiliki beberapa konstanta statis:

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

Lalu, saya ingin cara untuk mendapatkan string yang relevan berdasarkan konstanta:

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

Namun, ketika saya kompilasi, saya mendapatkan constant expression requiredkesalahan pada masing-masing 3 label kasus.

Saya mengerti bahwa kompiler memerlukan ekspresi untuk diketahui pada waktu kompilasi untuk mengkompilasi switch, tetapi mengapa tidak Foo.BA_konstan?


1
Adakah alasan untuk tidak menggunakan enum dalam kasus ini?
barrowc

1
Saya tidak berpikir Jawa memiliki enum. public static final intS tersebar di seluruh JDK, jadi itulah yang saya ikuti.
Austin Hyde


4
Dan bacalah Java Efektif ( java.sun.com/docs/books/effective ), Butir 30: Gunakan enum alih-alih konstanta int
Sean Patrick Floyd

Terima kasih untuk tipsnya guys, saya akan memeriksanya.
Austin Hyde

Jawaban:


150

Saya mengerti bahwa kompiler memerlukan ekspresi untuk diketahui pada waktu kompilasi untuk mengkompilasi switch, tetapi mengapa Foo.BA_ tidak konstan?

Sementara mereka konstan dari perspektif kode apa pun yang dieksekusi setelah bidang telah diinisialisasi, mereka bukan konstanta waktu kompilasi dalam pengertian yang diperlukan oleh JLS; lihat §15.28 Ekspresi Konstan untuk spesifikasi ekspresi konstan 1 . Ini mengacu pada §4.12.4 Variabel Final yang mendefinisikan "variabel konstan" sebagai berikut:

Kami menyebut variabel, dari tipe primitif atau tipe String, yang final dan diinisialisasi dengan ekspresi konstanta waktu kompilasi (§15.28) sebagai variabel konstan. Apakah suatu variabel adalah variabel konstan atau tidak dapat memiliki implikasi sehubungan dengan inisialisasi kelas (§12.4.1), kompatibilitas biner (§13.1, §13.4.9) dan penugasan yang pasti (§16).

Dalam contoh Anda, variabel Foo.BA * tidak memiliki inisialisasi, dan karenanya tidak memenuhi syarat sebagai "variabel konstan". Cara mengatasinya sederhana; mengubah deklarasi variabel Foo.BA * menjadi inisialisasi yang kompilasi ekspresi konstan waktu.

Dalam contoh lain (di mana inisialisasi sudah mengkompilasi ekspresi konstan waktu), menyatakan variabel sebagai finalapa yang diperlukan.

Anda bisa mengubah kode Anda untuk menggunakan konstanta enumdaripada intkonstanta, tetapi itu membawa pasangan lain dari batasan berbeda:

  • Anda harus memasukkan defaultkasing, meskipun jika Anda memiliki caseuntuk setiap nilai kas yang diketahui enum; lihat Mengapa standar diperlukan untuk sakelar enum?
  • The caselabel semua harus eksplisit enumnilai-nilai, bukan ekspresi yang mengevaluasi enumnilai-nilai.

1 - Batasan ekspresi konstan dapat diringkas sebagai berikut. Ekspresi konstan a) dapat menggunakan tipe primitif dan Stringhanya, b) memungkinkan pemilihan primer yang literal (terpisah dari null) dan variabel konstan saja, c) memungkinkan ekspresi konstan yang mungkin ditandai sebagai subekspresi, d) mengizinkan operator kecuali operator penugasan ++,, --atau instanceof, dan e) memungkinkan gips tipe ke tipe primitif atau Stringhanya.

Perhatikan bahwa ini tidak termasuk segala bentuk metode atau panggilan lambda new,, .class. .lengthatau array subscript. Lebih lanjut, setiap penggunaan nilai array, enumnilai, nilai tipe pembungkus primitif, tinju dan unboxing semuanya dikecualikan karena a).


79

Anda mendapatkan ekspresi Konstan yang diperlukan karena Anda meninggalkan nilai dari konstanta Anda. Mencoba:

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

48

Saya mendapatkan kesalahan ini di Android, dan solusi saya hanya menggunakan:

public static final int TAKE_PICTURE = 1;

dari pada

public static int TAKE_PICTURE = 1;

3
Hanya untuk klarifikasi: Ini menyelesaikan kesalahan Anda dengan membuat final properti statis. Dalam pertanyaan awal saya, masalahnya adalah bahwa properti statis akhir tidak memiliki penginisialisasi, menjadikannya konstan, tetapi bukan konstanta waktu kompilasi. Lihat jawaban yang diterima untuk detailnya.
Austin Hyde

4
Saya tahu ini masalah yang berbeda, tetapi karena saya tiba di sini dengan masalah saya, itu bisa membantu orang lain dalam situasi yang sama.
Teo Inke

Masuk akal mereka harus final karena semuanya akan salah jika nilai-nilai ini dapat mengubah runtime.
slot

31

Karena mereka tidak menyusun konstanta waktu. Pertimbangkan kode yang valid berikut ini:

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

Anda hanya bisa mengetahui nilai BARdalam runtime.


1
Menarik. Akan public static final int BAR = new Random().nextInt()bekerja
Thilo

4
Pernyataan Thilo mengkompilasi tetapi pernyataan switch mengeluh ekspresi konstan yang diperlukan . Lebih lanjut, tidak bisakah dua berturut-turut new Random().nextInt()mengembalikan nilai yang sama?
Tony Ennis

2
@Tony: Mana yang bagus. Itu tidak mengkompilasi karena tidak diinisialisasi dengan konstanta waktu kompilasi. Lihat jawaban Stephen yang diterima. Jika itu dikompilasi, integer acak akan dikodekan ke dalam kelas, dengan hasil yang sangat tidak terduga.
Thilo

Saya terkejut konstanta di saklar ditolak, dan 'konstan' itu sendiri tidak. Saya tidak pernah menyangka akan seperti ini. Tentu saja, itu tidak benar-benar konstan kurasa.
Tony Ennis

@TonyEnnis - Tergantung pada apa yang Anda maksud dengan benar-benar konstan. Ini benar-benar konstan dalam arti bahwa itu tidak akan berubah selama pelaksanaan program (modulo beberapa quibbles). Tapi itu tidak sama untuk semua eksekusi.
Stephen C

17

Anda dapat menggunakan enum seperti dalam contoh ini:

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

Sumber: Ganti pernyataan dengan enum


Hai, saya masih mengalami masalah menggunakan enum dengan cara ini: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> Ketika saya mencoba menggunakan enum di saklar saya mendapatkan kesalahan yang sama ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> Apakah mungkin untuk menyelesaikan masalah?
shaolin

1
@stiga - Anda hanya dapat mengaktifkan instance enum sendiri. Tidak pada beberapa nilai yang dikembalikan dengan memanggil metode pada instance enum.
Stephen C

3

Ini dijawab berabad-abad yang lalu dan mungkin tidak relevan, tetapi untuk berjaga-jaga. Ketika saya dihadapkan dengan masalah ini, saya hanya menggunakan ifpernyataan dan bukannya switch, itu memecahkan kesalahan. Ini tentu saja merupakan solusi dan mungkin bukan solusi yang "tepat", tetapi dalam kasus saya itu sudah cukup.


4
Ini adalah solusi dan bukan jawaban untuk pertanyaan
J. Doe

Mengapa saya terus mendapatkan suara di sini? ini solusi sah
Samer Murad

2
mungkin karena ini adalah pernyataan IF yang secara khusus kami coba hindari dengan switch
Dean Wild

1
Saya memilih karena pertanyaan di sini bukanlah "bagaimana" untuk menyelesaikan masalah, tetapi "mengapa" masalah terjadi. Saya pikir jawaban Anda di luar konteks. Juga jika Anda perfeksionis, Anda harus menyadari bahwa switchumumnya lebih cepat daripada panjang if-else, karena switchhanya memeriksa kondisi satu kali , sementara dengan if-elseAnda mungkin perlu memeriksa semua kondisi sebelum menemukan yang tepat.
Christian Lim

0

Terkadang variabel switch juga bisa membuat kesalahan itu misalnya:

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

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

   //...
}

Untuk menyelesaikannya Anda harus melemparkan variabel ke int (dalam hal ini). Begitu:

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

   case 0: //No Error

   //...
}

0

Mendapat kesalahan ini di Android saat melakukan sesuatu seperti ini:

 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

            }

meskipun mendeklarasikan konstanta:

public static final String ADMIN_CONSTANT= "Admin";

Saya menyelesaikan masalah dengan mengubah kode saya menjadi ini:

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

Dalam kasus saya, saya mendapatkan pengecualian ini karena

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

dalam kasus kedua saya memanggil konstanta dari instance var.MODIFICAR_KM:tetapi saya harus menggunakan VariablesKmDialog.OBTENER_KMlangsung dari kelas.


0

Jika Anda menggunakannya dalam sakelar maka Anda harus mendapatkan tipe enum bahkan sebelum Anda memasukkan nilai itu ke sakelar. Misalnya :

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

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

Dan enum seperti:

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

Kode di bawah ini cukup jelas. Kita dapat menggunakan enum dengan case switch:

/**
 *
 */
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;
    }
}

Berdasarkan nilai-nilai kelas dari enum dapat dipetakan:

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

    }

Semoga bermanfaat :)


0

Saya sarankan menggunakan cara berikut:

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

Saya pikir enum harus memiliki konstruktor berikut: private Animal(String name) { this.name = name; }
user1364368

-1

Saya merekomendasikan Anda untuk menggunakan enum :)

Lihat ini:

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

Maka Anda dapat menggunakannya seperti ini:

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

@ Djangofan apa versi JDK Anda menjalankan kode Anda?
everton

Saya menggunakan JDK 1.7.0_74 dengan IntelliJ-IDEA 14
djangofan

1
Saya menggunakan kelas yang sama seperti yang disarankan oleh Everton Agner tetapi menunjukkan ekspresi konstan yang diperlukan.
Amit Kumar
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.