Оператор перемикання 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 є перерахунки. public static final ints розкидані по всьому JDK, так що саме з цим я пішов.
— Остін Гайд


4
І читайте Ефективну Java ( java.sun.com/docs/books/effective ), пункт 30: Використовуйте enums замість int-констант
— Шон Патрік Флойд

Дякую за поради, хлопці, я перевірю їх.
— Остін Гайд

Відповіді:


150

Я розумію, що компілятору потрібен вираз, який потрібно знати під час компіляції, щоб скласти комутатор, але чому Foo.BA_ не є постійним?

Хоча вони постійні з точки зору будь-якого коду, який виконується після ініціалізації полів, вони не є константою часу компіляції у тому сенсі, який вимагає JLS; див. § 15.28 Постійні вирази для уточнення константного виразу 1 . Це стосується §4.12.4 Кінцеві змінні, яка визначає "постійну змінну" наступним чином:

Ми називаємо змінну, примітивного типу або типу String, яка є остаточною і ініціалізується постійним виразом часу компіляції (§15.28) постійною змінною. Будь змінна є постійною змінною чи ні, може мати наслідки щодо ініціалізації класів (§12.4.1), бінарної сумісності (§13.1, §13.4.9) та визначеного призначення (§16).

У вашому прикладі змінні Foo.BA * не мають ініціалізаторів, а значить, не кваліфікуються як "постійні змінні". Виправлення просте; змінити декларації змінної Foo.BA * на ініціалізатори, які є постійними виразами часу компіляції.

В інших прикладах (де ініціалізатори вже є постійними виразами часу компіляції), оголошуючи змінну такою, що finalможе бути необхідною.

Ви можете змінити свій код, щоб enumскоріше використовувати intконстанти, але це приносить ще кілька різних обмежень:


1 - Обмеження постійного вираження можна узагальнити наступним чином. Постійні вирази a) можуть використовувати примітивні типи і Stringлише, b) дозволяють праймери, які є буквальними (крім null) та лише постійними змінними, c) дозволяють постійні вирази, можливо, укрупнені як субекспресії; d) дозволяють операторам за винятком операторів призначення ++, --або instanceof, і д) дозволяють виконувати типові типи 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
Просто для уточнення: це вирішує вашу помилку, роблячи статичну властивість остаточною. У моєму первісному питанні, проблема полягала в тому, що в кінцевому статичному властивості відсутній ініціалізатор, що робить його постійним, але не константою часу компіляції. Деталі див. У прийнятій відповіді.
— Остін Гайд

4
Я знаю, що це інша проблема, але оскільки я потрапив сюди зі своєю, міг би допомогти комусь іншому в тій же ситуації.
— Тео Інке

Має сенс, що вони повинні бути остаточними, оскільки все піде не так, якби ці значення могли змінити час виконання.
— слот

31

Тому що це не константи часу компіляції. Розглянемо наступний дійсний код:

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

Ви можете знати лише значення BARчасу виконання.


1
Цікаво. Було б public static final int BAR = new Random().nextInt()працювати?
— Тіло

4
Заява Тіло компілюється, але оператор перемикання скаржиться на необхідне постійне вираження . Крім того, не могли два поспіль new Random().nextInt()повернути однакові значення?
— Тоні Енніс

2
@Tony: Що добре. Він не компілюється, тому що не ініціалізований константою часу компіляції. Дивіться прийняту відповідь Стівена. Якби це компілювали, випадкове ціле число було б жорстко закодовано в клас із досить непередбачуваними результатами.
— Тіло

Я здивований, що константа в комутаторі відхиляється, а сама "константа" - ні. Я ніколи б не думав, що так буде. Звичайно, я не думаю, що це справді константа.
— Тоні Енніс

@TonyEnnis - Це залежить від того, що ти маєш на увазі по-справжньому постійним. Це по-справжньому постійне в тому сенсі, що воно не зміниться під час виконання програми (по модулю пару каламбурів). Але це не однаково для всіх страт.
— Стівен С

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

Джерело: Переключення заяви на 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/> Можна вирішити проблему?
— shaolin

1
@stiga - Ви можете вмикати лише самі екземпляри перерахунків. Не на якесь значення, повернене за допомогою виклику методу на екземпляри enum.
— Стівен С

3

На це відповіли віки тому і, мабуть, не актуально, але про всяк випадок. Коли я стикався з цією проблемою, я просто використав ifоператор замість switch, це вирішило помилку. Це, звичайно, обхідне рішення і, мабуть, не «правильне» рішення, але в моєму випадку це було достатньо.


4
Це обхід, а не відповідь на запитання
— Дж. Дой

Чому я продовжую отримувати голоси тут? це законне рішення
— Самер Мурад

2
Мабуть тому, що це твердження IF, якого ми спеціально намагаємось уникати за допомогою перемикача
— Дін Уайлд,

1
Я проголосував, тому що тут питання не в тому, як "вирішити" проблему, а "чому" проблема. Я думаю, що ваша відповідь поза контекстом. Крім того, якщо ви перфекціоніст, ви повинні усвідомити, що switchце, як правило, швидше, ніж довго if-else, тому що switchперевіряйте умову лише один раз , тоді як разом з if-elseвами може знадобитися перевірити всі умови, перш ніж знайти потрібну.
— Крістіан Лім

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

Якщо ви використовуєте його в корпусі комутатора, вам потрібно отримати тип перерахунку ще до того, як підключити це значення до комутатора. Наприклад :

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 із корпусом комутатора:

/**
 *
 */
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 має бути такий конструктор: 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 використовується ваш код?
— Евертон

Я використовував JDK 1.7.0_74 з IntelliJ-IDEA 14
— djangofan

1
Я використовую той самий клас, що запропонований Евертоном Агнером, але його показник вимагає постійного вираження.
— Аміт Кумар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.