Câu lệnh chuyển đổi Java: Yêu cầu biểu thức không đổi, nhưng nó là hằng số


174

Vì vậy, tôi đang làm việc trên lớp này có một vài hằng số tĩnh:

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

Sau đó, tôi muốn một cách để có được một chuỗi có liên quan dựa trên hằng số:

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

Tuy nhiên, khi tôi biên dịch, tôi gặp constant expression requiredlỗi trên mỗi 3 trường hợp nhãn.

Tôi hiểu rằng trình biên dịch cần biết biểu thức tại thời điểm biên dịch để biên dịch một chuyển đổi, nhưng tại sao không phải là Foo.BA_hằng số?


1
Bất kỳ lý do không sử dụng một enum trong trường hợp này?
barrowc

1
Tôi không nghĩ Java có enum. public static final ints được phân tán tất cả thông qua JDK, vì vậy đó là những gì tôi đã đi với.
Austin Hyde


4
Và đọc Java hiệu quả ( java.sun.com/docs/books/effective ), Mục 30: Sử dụng enum thay cho hằng số int
Sean Patrick Floyd

Cảm ơn các bạn, tôi sẽ kiểm tra chúng.
Austin Hyde

Câu trả lời:


150

Tôi hiểu rằng trình biên dịch cần biết biểu thức tại thời điểm biên dịch để biên dịch một chuyển đổi, nhưng tại sao Foo.BA_ không đổi?

Mặc dù chúng là hằng số theo quan điểm của bất kỳ mã nào thực thi sau khi các trường đã được khởi tạo, chúng không phải là hằng số thời gian biên dịch theo nghĩa được yêu cầu bởi JLS; xem §15.28 Biểu thức hằng cho đặc điểm kỹ thuật của biểu thức hằng 1 . Điều này đề cập đến §4.12.4 Biến cuối cùng xác định "biến không đổi" như sau:

Chúng tôi gọi một biến, kiểu nguyên thủy hoặc kiểu chuỗi, là cuối cùng và được khởi tạo với biểu thức hằng số thời gian biên dịch (§15.28) là biến không đổi. Việc một biến có phải là biến không đổi hay không có thể có ý nghĩa đối với việc khởi tạo lớp (§12.4.1), khả năng tương thích nhị phân (§13.1, §13.4.9) và gán xác định (§16).

Trong ví dụ của bạn, các biến Foo.BA * không có bộ khởi tạo và do đó không đủ điều kiện là "biến không đổi". Cách khắc phục rất đơn giản; thay đổi khai báo biến Foo.BA * để có các khởi tạo là các biểu thức hằng số thời gian biên dịch.

Trong các ví dụ khác (trong đó các bộ khởi tạo đã là các biểu thức hằng số thời gian biên dịch), việc khai báo biến là finalcó thể là điều cần thiết.

Bạn có thể thay đổi mã của mình để sử dụng enumthay vì inthằng số, nhưng điều đó mang đến một vài hạn chế khác nhau:


1 - Các hạn chế biểu thức không đổi có thể được tóm tắt như sau. Các biểu thức hằng a) có thể sử dụng các kiểu nguyên thủy và Stringchỉ, b) cho phép các nguyên hàm là các chữ (ngoài null) và các biến không đổi, c) cho phép các biểu thức không đổi có thể được ngoặc đơn như là các biểu thức con, d) cho phép các toán tử trừ các toán tử gán ++, --hoặc instanceof, và e) cho phép các kiểu phôi thành các kiểu nguyên thủy hoặc Stringchỉ.

Lưu ý rằng điều này không bao gồm bất kỳ hình thức gọi hoặc phương thức lambda nào new, .class. .lengthhoặc đăng ký mảng. Hơn nữa, bất kỳ việc sử dụng các giá trị mảng, enumgiá trị, giá trị của các loại trình bao bọc nguyên thủy, quyền anh và unboxing đều bị loại trừ vì a).


79

Bạn nhận được biểu thức Constant cần thiết vì bạn đã bỏ các giá trị khỏi hằng số của mình. Thử:

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

48

Tôi đã gặp lỗi này trên Android và giải pháp của tôi chỉ là sử dụng:

public static final int TAKE_PICTURE = 1;

thay vì

public static int TAKE_PICTURE = 1;

3
Chỉ cần làm rõ: Điều này giải quyết lỗi của bạn bằng cách tạo một thuộc tính tĩnh cuối cùng. Trong câu hỏi ban đầu của tôi, vấn đề là thuộc tính tĩnh cuối cùng bị thiếu bộ khởi tạo, làm cho nó là một hằng số, nhưng không phải là hằng số thời gian biên dịch. Xem câu trả lời được chấp nhận để biết chi tiết.
Austin Hyde

4
Tôi biết đó là một vấn đề khác, nhưng vì tôi đến đây với tôi nên nó có thể giúp đỡ người khác trong tình huống tương tự.
Teo Inke 12/03/2015

Có nghĩa là họ phải là người cuối cùng vì mọi thứ sẽ sai nếu những giá trị này có thể thay đổi thời gian chạy.
slott

31

Bởi vì chúng không phải là hằng số thời gian biên dịch. Hãy xem xét các mã hợp lệ sau đây:

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

Bạn chỉ có thể biết giá trị của BARtrong thời gian chạy.


1
Hấp dẫn. Sẽ public static final int BAR = new Random().nextInt()làm việc?
Thilo

4
Câu lệnh của Thilo biên dịch nhưng câu lệnh chuyển đổi phàn nàn biểu thức không đổi cần thiết . Hơn nữa, không thể hai liên tiếp new Random().nextInt()trả lại cùng một giá trị?
Tony Enni

2
@Tony: Đó là một điều tốt. Nó không biên dịch vì nó không được khởi tạo với hằng số thời gian biên dịch. Xem câu trả lời được chấp nhận của Stephen. Nếu điều đó được biên dịch, một số nguyên ngẫu nhiên sẽ được mã hóa cứng vào lớp, với kết quả khá khó đoán.
Thilo

Tôi ngạc nhiên khi hằng số trong công tắc bị từ chối và bản thân 'hằng số' thì không. Tôi không bao giờ nghĩ rằng nó sẽ theo cách này. Tất nhiên, nó không thực sự là một hằng số tôi cho là.
Tony Enni

@TonyEnnis - Nó phụ thuộc vào những gì bạn có nghĩa là thực sự không đổi. Nó thực sự không đổi theo nghĩa là nó sẽ không thay đổi trong quá trình thực hiện chương trình (modulo một vài phân minh). Nhưng nó không giống nhau cho tất cả các vụ hành quyết.
Stephen C

17

Bạn có thể sử dụng một enum như trong ví dụ này:

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

Nguồn: Chuyển đổi câu lệnh với enum


Xin chào, tôi vẫn gặp sự cố khi sử dụng enum theo cách này: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> Khi tôi cố gắng sử dụng enum trong công tắc, tôi gặp lỗi tương tự ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> Có thể giải quyết vấn đề không?
shaolin

1
@stiga - Bạn chỉ có thể tự bật các trường hợp enum. Không phải trên một số giá trị được trả về bằng cách gọi một phương thức trên các trường hợp enum.
Stephen C

3

Điều này đã được trả lời từ lâu và có lẽ không liên quan, nhưng chỉ trong trường hợp. Khi tôi phải đối mặt với vấn đề này, tôi chỉ đơn giản là sử dụng một iftuyên bố thay vì switch, nó đã giải quyết được lỗi. Tất nhiên đó là một cách giải quyết và có lẽ không phải là giải pháp "đúng", nhưng trong trường hợp của tôi, nó là vừa đủ.


4
Đây là một cách giải quyết và không phải là câu trả lời cho câu hỏi
J. Doe

Tại sao tôi cứ bị bỏ phiếu ở đây? đó là một cách giải quyết hợp pháp
Samer Murad

2
có lẽ bởi vì đó là một tuyên bố IF mà chúng tôi đang cố gắng tránh bằng một công tắc
Dean Wild

1
Tôi đã bỏ phiếu vì câu hỏi ở đây không phải là "làm thế nào" để giải quyết vấn đề, mà là "tại sao" vấn đề xảy ra. Tôi nghĩ rằng câu trả lời của bạn là ra khỏi bối cảnh. Ngoài ra nếu bạn là người cầu toàn, bạn nên nhận ra rằng switchnó thường nhanh hơn thời gian dài if-else, bởi vì switchchỉ kiểm tra điều kiện một lần , trong khi với if-elsebạn có thể cần kiểm tra tất cả các điều kiện trước khi tìm đúng.
Christian Lim

0

Đôi khi, biến chuyển đổi cũng có thể gây ra lỗi đó, ví dụ:

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

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

   //...
}

Để giải quyết bạn nên truyền biến thành int (trong trường hợp này). Vì thế:

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

   case 0: //No Error

   //...
}

0

Có lỗi này trong Android khi đang làm một cái gì đó như thế này:

 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

            }

mặc dù tuyên bố một hằng số:

public static final String ADMIN_CONSTANT= "Admin";

Tôi đã giải quyết vấn đề bằng cách thay đổi mã của mình thành điều này:

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

Trong trường hợp của tôi, tôi đã nhận được ngoại lệ này bởi vì

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

trong trường hợp thứ hai tôi đã gọi hằng số từ thể hiện var.MODIFICAR_KM:nhưng tôi nên sử dụng VariablesKmDialog.OBTENER_KMtrực tiếp từ lớp.


0

Nếu bạn đang sử dụng nó trong trường hợp chuyển đổi thì bạn cần phải lấy loại enum ngay cả trước khi bạn cắm giá trị đó vào công tắc. Ví dụ :

Một sốEnum someEnum = someEnum.values ​​() [1];

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

Và enum giống như:

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

Mã bên dưới là tự giải thích, Chúng ta có thể sử dụng một enum với trường hợp chuyển đổi:

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

Dựa trên các giá trị lớp từ enum có thể được ánh xạ:

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

    }

Hy vọng nó giúp :)


0

Tôi khuyên bạn nên sử dụng cách sau:

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

Tôi nghĩ enum nên có hàm tạo sau: private Animal(String name) { this.name = name; }
user1364368

-1

Tôi khuyên bạn nên sử dụng enums :)

Kiểm tra này:

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

Sau đó, bạn có thể sử dụng nó như thế này:

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

@djangofan Phiên bản JDK nào bạn đang chạy mã của mình?
everton

Tôi đã sử dụng JDK 1.7.0_74 với IntelliJ-IDEA 14
djangofan

1
Tôi đang sử dụng cùng một lớp theo đề xuất của Everton Agner nhưng nó thể hiện biểu thức không đổi.
Amit Kumar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.