คำสั่งสวิตช์ Java: ต้องการนิพจน์คงที่ แต่เป็นค่าคงที่


175

ดังนั้นฉันกำลังทำงานในชั้นนี้ที่มีค่าคงที่ไม่กี่:

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
เหตุผลใดที่จะไม่ใช้ enum ในกรณีนี้
barrowc

1
ฉันไม่คิดว่า Java มี enums public static final intมีกระจายอยู่ทั่ว JDK ดังนั้นนั่นคือสิ่งที่ฉันไปด้วย
Austin Hyde


4
และอ่านจาวาที่มีประสิทธิภาพ ( java.sun.com/docs/books/effective ), รายการที่ 30: ใช้ enums แทนค่าคงที่ int
Sean Patrick Floyd

ขอบคุณสำหรับเคล็ดลับพวกฉันจะตรวจสอบเหล่านั้นออก
Austin Hyde

คำตอบ:


150

ฉันเข้าใจว่าคอมไพเลอร์ต้องการให้นิพจน์เป็นที่รู้จักในเวลารวบรวมเพื่อรวบรวมสวิตช์ แต่ทำไม Foo.BA_ ไม่คงที่

ในขณะที่ค่าคงที่จากมุมมองของโค้ดใด ๆ ที่เรียกใช้งานหลังจากที่ฟิลด์ได้รับการเริ่มต้นพวกเขาจะไม่คงที่เวลารวบรวมในแง่ที่จำเป็นโดย JLS; ดู§15.28นิพจน์คงที่สำหรับคุณสมบัติของการแสดงออกคงที่ 1 สิ่งนี้อ้างถึง§4.12.4ตัวแปรสุดท้ายซึ่งกำหนด "ตัวแปรคงที่" ดังนี้:

เราเรียกตัวแปรชนิดดั้งเดิมหรือประเภทสตริงที่สุดท้ายและเริ่มต้นด้วยการแสดงออกคงที่เวลารวบรวม (§15.28) ตัวแปรคงที่ ไม่ว่าตัวแปรนั้นจะเป็นตัวแปรคงที่หรือไม่อาจมีความเกี่ยวข้องกับการกำหนดค่าเริ่มต้นของคลาส (§12.4.1), ความเข้ากันได้ของไบนารี (§13.1, §13.4.9) และการกำหนดที่แน่นอน (§16)

ในตัวอย่างของคุณตัวแปร Foo.BA * ไม่มี initializers และดังนั้นจึงไม่ถือว่าเป็น "ตัวแปรคงที่" การแก้ไขนั้นง่าย เปลี่ยนการประกาศตัวแปร Foo.BA * เพื่อให้มี initializers ที่เป็นนิพจน์ค่าคงที่แบบคอมไพล์เวลา

ในตัวอย่างอื่น ๆ (ซึ่ง initializers เป็นนิพจน์ค่าคงที่เวลาคอมไพล์แล้ว) ประกาศตัวแปรตามที่finalอาจเป็นสิ่งที่จำเป็น

คุณสามารถเปลี่ยนรหัสของคุณเพื่อใช้ค่าคงที่ได้enumมากกว่าintแต่ก็มีข้อ จำกัด ที่แตกต่างกันสองข้อ:


1 - ข้อ จำกัด นิพจน์คงที่สามารถสรุปได้ดังนี้ การแสดงออกอย่างต่อเนื่อง) สามารถใช้รูปแบบดั้งเดิมและStringเพียง b) อนุญาตให้พรรคที่มีตัวอักษร (นอกเหนือจากnull) และตัวแปรคงที่เท่านั้น c) อนุญาตให้มีการแสดงออกอย่างต่อเนื่องอาจ parenthesised เป็น subexpressions ง) ให้ผู้ประกอบการยกเว้นสำหรับผู้ประกอบการที่ได้รับมอบหมาย++, --หรือinstanceofและ e) อนุญาตให้ใช้การพิมพ์แบบStringธรรมดาหรือแบบดั้งเดิมเท่านั้น

newโปรดทราบว่านี้ไม่รวมถึงรูปแบบของวิธีการหรือแลมบ์ดาโทรใด ๆ.class, .lengthหรือการห้อยอาร์เรย์ นอกจากนี้การใช้ค่าอาร์เรย์, enumค่า, ค่าของประเภท wrapper ดั้งเดิมการชกมวยและการเลิกทำกล่องจะไม่รวมอยู่เนื่องจาก 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
เพื่อความกระจ่าง: สิ่งนี้จะแก้ไขข้อผิดพลาดของคุณโดยทำให้คุณสมบัติคงที่เป็นที่สิ้นสุด ในคำถามเดิมของฉันปัญหาคือว่าคุณสมบัติคงที่สุดท้ายหายไปเริ่มต้นทำให้มันคงที่ แต่ไม่ใช่ค่าคงที่เวลารวบรวม ดูคำตอบที่ยอมรับได้สำหรับรายละเอียด
Austin Hyde

4
ฉันรู้ว่ามันเป็นปัญหาที่แตกต่างกัน แต่เมื่อฉันมาที่นี่กับฉันมันจะช่วยคนอื่นในสถานการณ์เดียวกัน
Teo Inke

ทำให้รู้สึกว่าพวกเขาจะต้องจบเพราะสิ่งต่าง ๆ จะผิดถ้าค่าเหล่านี้สามารถเปลี่ยนรันไทม์
slott

31

เพราะสิ่งเหล่านั้นไม่ได้รวบรวมค่าคงที่เวลา พิจารณารหัสที่ถูกต้องต่อไปนี้:

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

คุณสามารถรู้คุณค่าของการBARใช้งานได้จริง


1
น่าสนใจ จะpublic static final int BAR = new Random().nextInt()ทำงานอย่างไร
Thilo

4
Thilo เป็นคอมไพล์คำสั่ง แต่คำสั่งเปลี่ยนบ่นแสดงออกคงต้อง ยิ่งกว่านั้นสองค่าติดๆกันไม่สามารถnew Random().nextInt()คืนค่าเดิมได้หรือไม่?
โทนี่เอนนิส

2
@ โทนี่: ซึ่งเป็นสิ่งที่ดี มันไม่ได้รวบรวมเพราะมันไม่ได้เริ่มต้นกับค่าคงที่เวลารวบรวม ดูคำตอบที่ยอมรับของสตีเฟ่น ถ้ามันทำการคอมไพล์จำนวนเต็มแบบสุ่มจะถูกเข้ารหัสยากในคลาสพร้อมกับผลลัพธ์ที่ไม่สามารถคาดเดาได้
Thilo

ฉันประหลาดใจที่ค่าคงที่ในสวิตช์ถูกปฏิเสธและ 'ค่าคงที่' นั้นไม่ได้เป็นเช่นนั้น ฉันไม่เคยคิดว่ามันจะเป็นเช่นนี้ แน่นอนว่าฉันคิดว่ามันคงไม่เป็นเช่นนั้นเสมอไป
Tony Ennis

@TonyEnnis - มันขึ้นอยู่กับสิ่งที่คุณหมายถึงโดยค่าคงที่อย่างแท้จริง มันคงที่อย่างแท้จริงในแง่ที่ว่ามันจะไม่เปลี่ยนแปลงในระหว่างการดำเนินการของโปรแกรม (modulo สองสาม quibbles) แต่มันไม่เหมือนกันสำหรับการประหารชีวิตทั้งหมด
สตีเฟ่นซี

17

คุณสามารถใช้ enum เหมือนในตัวอย่างนี้:

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 statement ด้วย enum


สวัสดีฉันยังคงมีปัญหาในการใช้ enum ด้วยวิธีนี้: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> เมื่อฉันพยายามใช้ enum ในสวิตช์ฉันได้รับข้อผิดพลาดเดียวกัน ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> เป็นไปได้ที่จะแก้ปัญหาหรือไม่
เส้าหลิน

1
@stiga - คุณสามารถเปิดอินสแตนซ์ enum ได้เท่านั้น ไม่ใช่ในบางค่าที่ส่งคืนโดยการเรียกใช้เมธอดบน enum อินสแตนซ์
สตีเฟ่นซี

3

นี่เป็นคำตอบมานานแล้วและอาจไม่เกี่ยวข้องเลย แต่ในกรณีนี้ เมื่อฉันเผชิญหน้ากับปัญหานี้ฉันเพียงแค่ใช้ifคำสั่งแทนswitchมันแก้ไขข้อผิดพลาด แน่นอนว่ามันเป็นวิธีแก้ปัญหาและอาจไม่ใช่วิธีที่ "ถูกต้อง" แต่ในกรณีของฉันมันก็เพียงพอแล้ว


4
นี่เป็นวิธีแก้ปัญหาและไม่ใช่คำตอบสำหรับคำถาม
J. Doe

ทำไมฉันถึงได้ลงคะแนนที่นี่? มันเป็นวิธีแก้ปัญหาที่ถูกกฎหมาย
Samer Murad

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

ในกรณีที่สองฉันเรียกค่าคงที่จากอินสแตนซ์var.MODIFICAR_KM:แต่ฉันควรใช้VariablesKmDialog.OBTENER_KMโดยตรงจากคลาส


0

หากคุณใช้มันในกรณีสวิตช์คุณจะต้องรับค่าชนิดของ enum ก่อนที่คุณจะเสียบค่านั้นในสวิตช์ ตัวอย่างเช่น

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

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

และ enum เป็นเช่น:

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 กับตัวเรือนสวิตช์ได้:

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

ฉันคิดว่า enum ควรมี Constructor ต่อไปนี้: private Animal(String name) { this.name = name; }
1364368

-1

ฉันแนะนำให้คุณใช้ enums :)

ลองดู:

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 เวอร์ชั่นใดอยู่
Everton

ฉันใช้ JDK 1.7.0_74 กับ IntelliJ-IDEA 14
djangofan

1
ฉันใช้คลาสเดียวกันกับที่แนะนำโดย Everton Agner แต่ต้องแสดงนิพจน์คงที่
Amit Kumar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.