Instruction de commutation Java: expression constante requise, mais elle EST constante

175

Donc, je travaille sur cette classe qui a quelques constantes statiques:

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

Ensuite, je voudrais un moyen d'obtenir une chaîne pertinente basée sur la constante:

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

Cependant, lorsque je compile, j'obtiens une constant expression requirederreur sur chacune des 3 étiquettes de cas.

Je comprends que le compilateur a besoin que l'expression soit connue au moment de la compilation pour compiler un commutateur, mais pourquoi n'est-elle pas Foo.BA_constante?

Austin Hyde
la source
1
Une raison de ne pas utiliser une énumération dans ce cas?
barrowc
1
Je ne pensais pas que Java avait des énumérations. public static final ints sont dispersés dans tout le JDK, c'est donc ce que j'ai choisi.
Austin Hyde
4
Et lisez Effective Java ( java.sun.com/docs/books/effective ), Item 30: Use enums au lieu de int constants
Sean Patrick Floyd
Merci pour les conseils les gars, je vais les vérifier.
Austin Hyde

Réponses:

150

Je comprends que le compilateur a besoin que l'expression soit connue au moment de la compilation pour compiler un commutateur, mais pourquoi Foo.BA_ n'est-il pas constant?

Bien qu'ils soient constants du point de vue de tout code qui s'exécute après que les champs ont été initialisés, ils ne sont pas une constante de temps de compilation au sens requis par le JLS; voir §15.28 Expressions constantes pour la spécification d'une expression constante 1 . Ceci fait référence au §4.12.4 Variables finales qui définit une "variable constante" comme suit:

Nous appelons une variable, de type primitif ou de type String, qui est finale et initialisée avec une expression constante à la compilation (§15.28) une variable constante. Le fait qu'une variable soit une variable constante ou non peut avoir des implications en ce qui concerne l'initialisation de classe (§12.4.1), la compatibilité binaire (§13.1, §13.4.9) et l'affectation définie (§16).

Dans votre exemple, les variables Foo.BA * n'ont pas d'initialiseurs et ne sont donc pas qualifiées de "variables constantes". Le correctif est simple; changez les déclarations de variable Foo.BA * pour avoir des initialiseurs qui sont des expressions constantes à la compilation.

Dans d'autres exemples (où les initialiseurs sont déjà des expressions constantes au moment de la compilation), déclarer la variable selon les finalbesoins.

Vous pouvez modifier votre code pour utiliser un enumplutôt que des intconstantes, mais cela apporte un autre couple de restrictions différentes:


1 - Les restrictions d'expression constante peuvent être résumées comme suit. Les expressions constantes a) peuvent utiliser des types primitifs et Stringseulement, b) autoriser les primaires qui sont des littéraux (en dehors de null) et des variables constantes uniquement, c) autoriser les expressions constantes éventuellement entre parenthèses comme des sous-expressions, d) autoriser les opérateurs sauf pour les opérateurs d'affectation ++, --ou instanceof, et e) autoriser les transtypages de types en types primitifs ou Stringuniquement.

Notez que cela ne comprend aucune forme d'appels de méthode ou lambda, new, .class. .lengthou indice de tableau. En outre, toute utilisation de valeurs de tableau, de enumvaleurs, de valeurs de types d'encapsuleurs primitifs, de mise en boîte et de déballage sont toutes exclues en raison de a).

Stephen C
la source
79

Vous obtenez l' expression constante requise car vous avez laissé les valeurs hors de vos constantes. Essayer:

public abstract class Foo {
    ...
    public static final int BAR=0;
    public static final int BAZ=1;
    public static final int BAM=2;
    ...
}
Tony Ennis
la source
48

J'ai eu cette erreur sur Android, et ma solution consistait simplement à utiliser:

public static final int TAKE_PICTURE = 1;

au lieu de

public static int TAKE_PICTURE = 1;
Teo Inke
la source
3
Juste pour clarifier: cela résout votre erreur en définissant une propriété statique. Dans ma question initiale, le problème était que la propriété statique finale manquait d'un initialiseur, ce qui en faisait une constante, mais pas une constante de compilation. Voir la réponse acceptée pour plus de détails.
Austin Hyde
4
Je sais que c'est un problème différent, mais depuis que je suis ici avec le mien, cela pourrait aider quelqu'un d'autre dans la même situation.
Teo Inke
Cela a du sens qu'ils doivent être définitifs car les choses iraient mal si ces valeurs pouvaient changer d'exécution.
slott
31

Parce que ce ne sont pas des constantes de temps de compilation. Considérez le code valide suivant:

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

Vous ne pouvez connaître la valeur de BARqu'en runtime.

Sheldon L. Cooper
la source
1
Intéressant. Serait public static final int BAR = new Random().nextInt()travailler?
Thilo
4
L'instruction de Thilo se compile mais l'instruction switch se plaint d' une expression constante requise . De plus, deux consécutifs ne pourraient-ils pas new Random().nextInt()renvoyer les mêmes valeurs?
Tony Ennis
2
@Tony: Ce qui est une bonne chose. Il ne compile pas car il n'est pas initialisé avec une constante de compilation. Voir la réponse acceptée de Stephen. Si cela se compilait, un entier aléatoire serait codé en dur dans la classe, avec des résultats assez imprévisibles.
Thilo
Je suis surpris que la constante du commutateur soit rejetée, et la «constante» elle-même ne l'est pas. Je n'aurais jamais pensé que ce serait de cette façon. Bien sûr, ce n'est pas vraiment une constante, je suppose.
Tony Ennis
@TonyEnnis - Cela dépend de ce que vous entendez par vraiment constant. Il est vraiment constant dans le sens où il ne changera pas lors de l'exécution du programme (modulo quelques chipotages). Mais il n'en est pas de même pour toutes les exécutions.
Stephen C
17

Vous pouvez utiliser une énumération comme dans cet exemple:

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

Source: instruction Switch avec enum

thénosique
la source
Salut, j'ai toujours le problème en utilisant l'énumération de cette manière: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> Quand j'essaye d'utiliser l'énumération dans le commutateur, j'obtiens la même erreur ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> Il est possible de résoudre le problème?
shaolin
1
@stiga - Vous ne pouvez activer que les instances enum elles-mêmes. Pas sur une valeur renvoyée en appelant une méthode sur les instances enum.
Stephen C
3

Cela a été répondu il y a longtemps et probablement pas pertinent, mais juste au cas où. Lorsque j'ai été confronté à ce problème, j'ai simplement utilisé une ifdéclaration au lieu de switch, cela a résolu l'erreur. C'est bien sûr une solution de contournement et probablement pas la «bonne» solution, mais dans mon cas, c'était juste suffisant.

Samer Murad
la source
4
Ceci est une solution de contournement et non une réponse à la question
J. Doe
Pourquoi est-ce que je continue d'obtenir des votes ici? c'est une solution de contournement légitime
Samer Murad
2
probablement parce que c'est une déclaration IF que nous essayons spécifiquement d'éviter avec un commutateur
Dean Wild
1
J'ai voté à la baisse parce que la question ici n'est pas "comment" résoudre le problème, mais "pourquoi" le problème est survenu. Je pense que votre réponse est hors contexte. Aussi, si vous êtes perfectionniste, vous devez comprendre que switchc'est généralement plus rapide que long if-else, car switchne vérifiez la condition qu'une seule fois , alors qu'avec if-elsevous devrez peut-être vérifier toutes les conditions avant de trouver la bonne.
Christian Lim
0

Parfois, la variable switch peut également faire cette erreur, par exemple:

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

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

   //...
}

Pour résoudre, vous devez convertir la variable en int (dans ce cas). Alors:

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

   case 0: //No Error

   //...
}
Mahdi-Malv
la source
0

Vous avez cette erreur dans Android en faisant quelque chose comme ceci:

 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

            }

malgré la déclaration d'une constante:

public static final String ADMIN_CONSTANT= "Admin";

J'ai résolu le problème en modifiant mon code comme suit:

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:

            }
Ojonugwa Jude Ochalifu
la source
0

Dans mon cas, j'obtenais cette exception car

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

dans le second cas, j'appelais la constante depuis l'instance var.MODIFICAR_KM:mais je devrais l'utiliser VariablesKmDialog.OBTENER_KMdirectement depuis la classe.

Gian Gomen
la source
0

Si vous l'utilisez dans un boîtier de commutateur, vous devez obtenir le type d'énumération avant même de brancher cette valeur dans le commutateur. Par exemple :

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

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

Et l'énumération est comme:

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;
    }
}
Akash Yellappa
la source
0

Le code ci-dessous est explicite, nous pouvons utiliser une énumération avec un cas de commutateur:

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

Basé sur les valeurs de classe de l'énumération peuvent être mappées:

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

    }

J'espère que ça aide :)

Mukundhan
la source
0

Je recommande d'utiliser la manière suivante:

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;
         }
     }
}
Dinesh
la source
Je pense que l'énumération devrait avoir le constructeur suivant: private Animal(String name) { this.name = name; }
user1364368
-1

Je vous recommande d'utiliser les énumérations :)

Regarde ça:

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

Ensuite, vous pouvez l'utiliser comme ceci:

System.out.println(Foo.BAR.getDescription());
Everton
la source
@djangofan sur quelle version JDK exécutez-vous votre code?
everton
J'ai utilisé JDK 1.7.0_74 avec IntelliJ-IDEA 14
djangofan
1
J'utilise la même classe que celle suggérée par Everton Agner mais son expression constante est requise.
Amit Kumar