Istruzione switch Java: espressione costante richiesta, ma è costante


175

Quindi, sto lavorando su questa classe che ha alcune costanti statiche:

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

Quindi, vorrei un modo per ottenere una stringa pertinente in base alla costante:

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

Tuttavia, quando compilo, ricevo un constant expression requirederrore su ciascuna delle 3 etichette del caso.

Capisco che il compilatore ha bisogno che l'espressione sia conosciuta al momento della compilazione per compilare uno switch, ma perché non è Foo.BA_costante?


1
Qualche motivo per non usare un enum in questo caso?
Barrowc,

1
Non pensavo che Java avesse enumerazioni. public static final intsono sparsi per tutto il JDK, quindi è quello che ho seguito.
Austin Hyde,


4
E leggi Effective Java ( java.sun.com/docs/books/effective ), voce 30: usa enum invece di costanti int
Sean Patrick Floyd

Grazie per i suggerimenti ragazzi, li controllerò.
Austin Hyde

Risposte:


150

Comprendo che il compilatore ha bisogno che l'espressione sia nota al momento della compilazione per compilare uno switch, ma perché Foo.BA_ non è costante?

Sebbene siano costanti dal punto di vista di qualsiasi codice che viene eseguito dopo l'inizializzazione dei campi, non sono una costante di tempo di compilazione nel senso richiesto da JLS; vedi §15.28 espressioni costanti per la specifica di una costante espressione 1 . Questo si riferisce al §4.12.4 Variabili finali che definisce una "variabile costante" come segue:

Chiamiamo una variabile, di tipo primitivo o tipo String, che è finale e inizializzata con un'espressione costante in fase di compilazione (§15.28) una variabile costante. Il fatto che una variabile sia una variabile costante o meno può avere implicazioni rispetto all'inizializzazione della classe (§12.4.1), compatibilità binaria (§13.1, §13.4.9) e assegnazione definita (§16).

Nel tuo esempio, le variabili Foo.BA * non hanno inizializzatori e quindi non si qualificano come "variabili costanti". La correzione è semplice; modificare le dichiarazioni delle variabili Foo.BA * in modo che abbiano inizializzatori che sono espressioni costanti in fase di compilazione.

In altri esempi (in cui gli inizializzatori sono già espressioni costanti in fase di compilazione), dichiarando la variabile come finalpotrebbe essere ciò che è necessario.

È possibile modificare il codice per utilizzare una enumanziché una intcostante, ma ciò comporta un altro paio di restrizioni diverse:


1 - Le restrizioni di espressione costante possono essere riassunte come segue. Espressioni costanti a) possono utilizzare tipi primitivi e Stringsolo, b) consentire primarie che sono letterali (a parte null) e variabili costanti solo, c) consentire espressioni costanti eventualmente parentesi, come sottoespressioni, d) consentire agli operatori tranne per operatori di assegnazione, ++, --o instanceof, e e) consentire il cast di tipi Stringsolo a tipi primitivi .

Si noti che questo non include alcuna forma di chiamate di metodo o di lambda, new, .class. .lengtho sottoscrizione di array. Inoltre, qualsiasi uso di valori di array, enumvalori, valori di tipi di wrapper primitivi, boxe e unboxing sono tutti esclusi a causa di a).


79

Ottieni l' espressione Costante richiesta perché hai lasciato i valori fuori dalle tue costanti. Provare:

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

48

Ho riscontrato questo errore su Android e la mia soluzione era solo quella di utilizzare:

public static final int TAKE_PICTURE = 1;

invece di

public static int TAKE_PICTURE = 1;

3
Solo per chiarimenti: questo risolve il tuo errore rendendo definitiva una proprietà statica. Nella mia domanda originale, il problema era che alla proprietà statica finale mancava un inizializzatore, rendendolo una costante, ma non una costante di compilazione. Vedi la risposta accettata per i dettagli.
Austin Hyde,

4
So che è un problema diverso, ma da quando sono arrivato qui con il mio potrebbe aiutare qualcun altro nella stessa situazione.
Teo Inke,

Ha senso che debbano essere definitivi poiché le cose andrebbero male se questi valori cambiassero il tempo di esecuzione.
Slott

31

Perché quelli non sono costanti di tempo di compilazione. Considera il seguente codice valido:

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

Puoi conoscere solo il valore di BARin runtime.


1
Interessante. Funzionerebbe public static final int BAR = new Random().nextInt()?
Thilo,

4
L'istruzione Thilo viene compilata ma l'istruzione switch si lamenta dell'espressione costante richiesta . Inoltre, due new Random().nextInt()valori consecutivi non potrebbero restituire gli stessi valori?
Tony Ennis,

2
@Tony: che è una buona cosa. Non viene compilato perché non è inizializzato con una costante di compilazione-tempo. Vedi la risposta accettata di Stephen. Se ciò fosse compilato, un intero casuale sarebbe codificato nella classe, con risultati piuttosto imprevedibili.
Thilo,

Sono sorpreso che la costante nell'interruttore sia rifiutata e la 'costante' stessa non lo è. Non avrei mai pensato che sarebbe stato così. Certo, suppongo non sia veramente una costante.
Tony Ennis,

@TonyEnnis - Dipende da cosa intendi veramente costante. È veramente costante, nel senso che non cambierà durante l'esecuzione del programma (modulo un paio di cavilli). Ma non è lo stesso per tutte le esecuzioni.
Stephen C,

17

Puoi usare un enum come in questo esempio:

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

Fonte: istruzione Switch con enum


Ciao, sto ancora riscontrando il problema usando l'enum in questo modo: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> Quando provo ad usare l'enum nello switch ottengo lo stesso errore ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> È possibile risolvere il problema?
Shaolin,

1
@stiga - Puoi attivare solo le istanze enum. Non su un valore restituito chiamando un metodo sulle istanze enum.
Stephen C

3

Questa è stata risposta anni fa e probabilmente non è rilevante, ma per ogni evenienza. Quando mi sono trovato di fronte a questo problema, ho semplicemente usato ifun'istruzione invece di switch, risolto l'errore. Naturalmente è una soluzione alternativa e probabilmente non è la soluzione "giusta", ma nel mio caso è stata appena sufficiente.


4
Questa è una soluzione alternativa e non una risposta alla domanda
J. Doe,

Perché continuo a ottenere voti qui? è una soluzione legittima
Samer Murad,

2
probabilmente perché è un'affermazione IF che stiamo specificamente cercando di evitare con un interruttore
Dean Wild,

1
Ho votato perché la domanda qui non è "come" risolvere il problema, ma "perché" si è verificato il problema. Penso che la tua risposta sia fuori contesto. Inoltre, se sei un perfezionista, dovresti rendertene conto che switchè generalmente più veloce che lungo if-else, perché switchcontrolla la condizione solo una volta , mentre con if-elsete potrebbe essere necessario controllare tutte le condizioni prima di trovare quella giusta.
Christian Lim,

0

A volte la variabile switch può anche fare questo errore, ad esempio:

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

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

   //...
}

Per risolvere dovresti lanciare la variabile in int (in questo caso). Così:

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

   case 0: //No Error

   //...
}

0

Ho ricevuto questo errore in Android mentre facevo qualcosa del genere:

 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

            }

nonostante abbia dichiarato una costante:

public static final String ADMIN_CONSTANT= "Admin";

Ho risolto il problema modificando il mio codice in questo:

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

Nel mio caso, stavo ottenendo questa eccezione perché

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

nel secondo caso stavo chiamando la costante dall'istanza var.MODIFICAR_KM:ma avrei dovuto usarla VariablesKmDialog.OBTENER_KMdirettamente dalla classe.


0

Se lo si utilizza in una custodia, è necessario ottenere il tipo di enum anche prima di inserire quel valore nello switch. Per esempio :

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

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

E l'enum è come:

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

Di seguito il codice è autoesplicativo, possiamo usare un enum con una custodia:

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

Sulla base dei valori di classe dall'enum può essere mappato:

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

    }

Spero che sia d'aiuto :)


0

Raccomando di usare il seguente modo:

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

Penso che l'enum dovrebbe avere il seguente costruttore: private Animal(String name) { this.name = name; }
user1364368

-1

Ti consiglio di usare enums :)

Controllalo:

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

Quindi puoi usarlo in questo modo:

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

@djangofan su quale versione di JDK stai eseguendo il tuo codice?
everton,

Ho usato JDK 1.7.0_74 con IntelliJ-IDEA 14
djangofan

1
Sto usando la stessa classe suggerita da Everton Agner ma è richiesta la sua espressione costante.
Amit Kumar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.