Java switch statement: Constant expression required, but it IS constant

273,733

Solution 1

I understand that the compiler needs the expression to be known at compile time to compile a switch, but why isn't Foo.BA_ constant?

While they are constant from the perspective of any code that executes after the fields have been initialized, they are not a compile time constant in the sense required by the JLS; see §15.28 Constant Expressions for the specification of a constant expression1. This refers to §4.12.4 Final Variables which defines a "constant variable" as follows:

We call a variable, of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28) a constant variable. Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9) and definite assignment (§16).

In your example, the Foo.BA* variables do not have initializers, and hence do not qualify as "constant variables". The fix is simple; change the Foo.BA* variable declarations to have initializers that are compile-time constant expressions.

In other examples (where the initializers are already compile-time constant expressions), declaring the variable as final may be what is needed.

You could change your code to use an enum rather than int constants, but that brings another couple of different restrictions:

  • You must include a default case, even if you have case for every known value of the enum; see Why is default required for a switch on an enum?
  • The case labels must all be explicit enum values, not expressions that evaluate to enum values.

1 - The constant expression restrictions can be summarized as follows. Constant expressions a) can use primitive types and String only, b) allow primaries that are literals (apart from null) and constant variables only, c) allow constant expressions possibly parenthesised as subexpressions, d) allow operators except for assignment operators, ++, -- or instanceof, and e) allow type casts to primitive types or String only.

Note that this doesn't include any form of method or lambda calls, new, .class. .length or array subscripting. Furthermore, any use of array values, enum values, values of primitive wrapper types, boxing and unboxing are all excluded because of a).

Solution 2

You get Constant expression required because you left the values off your constants. Try:

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

Solution 3

I got this error on Android, and my solution was just to use:

public static final int TAKE_PICTURE = 1;

instead of

public static int TAKE_PICTURE = 1;

Solution 4

Because those are not compile time constants. Consider the following valid code:

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

You can only know the value of BAR in runtime.

Solution 5

You can use an enum like in this example:

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: Switch statement with enum

Share:
273,733

Related videos on Youtube

Austin Hyde
Author by

Austin Hyde

I'm a software engineer, primarily doing work with PHP, Python, and JavaScript web applications, services and related technologies.

Updated on October 29, 2021

Comments

  • Austin Hyde
    Austin Hyde over 2 years

    So, I am working on this class that has a few static constants:

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

    Then, I would like a way to get a relevant string based on the constant:

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

    However, when I compile, I get a constant expression required error on each of the 3 case labels.

    I understand that the compiler needs the expression to be known at compile time to compile a switch, but why isn't Foo.BA_ constant?

    • barrowc
      barrowc over 13 years
      Any reason not to use an enum in this case?
    • Austin Hyde
      Austin Hyde over 13 years
      I didn't think Java had enums. public static final ints are scattered all through the JDK, so that's what I went with.
    • barrowc
      barrowc over 13 years
    • Sean Patrick Floyd
      Sean Patrick Floyd over 13 years
      And read Effective Java (java.sun.com/docs/books/effective), Item 30: Use enums instead of int constants
    • Austin Hyde
      Austin Hyde over 13 years
      Thanks for the tips guys, I will check those out.
    • Kevin Kostlan
      Kevin Kostlan almost 11 years
      Many solutions suggest enums, which are useful for more complex cases. It may not be worth making a whole new file, instead just set BAR=0;BAZ=1; etc.
    • Austin Hyde
      Austin Hyde about 8 years
      @FastSnail I wholeheartedly disagree. First, this question is about Java language constructs, that question is about Android development on the surface and actually about solving a class of problems with an enum. Second, this question was asked and answered two years before that question.
    • Madhawa Priyashantha
      Madhawa Priyashantha about 8 years
      @AustinHyde omg i can't believe i did this .i try to make a comment yesterday with a link .so user can see name like in above "select item ...".so i just made a comment in this page [i was reading this question ] .and i catch the format of link by marking as duplicate .then i edit the comment to see the format .i immediately retracted close vote.i forget to delete it.I'm so sorry for disturbing
    • Austin Hyde
      Austin Hyde about 8 years
      @FastSnail It's okay :)
    • Ti Strga
      Ti Strga over 4 years
      Late to the party, but just as a historical note: "I didn't think Java had enums. public static final ints are scattered all through the JDK, so that's what I went with." At the time the JDK was written, Java didn't have enums. That feature was added years later; changing the standard classes in the JDK/JRE would have messed with binary compatibility. (Classes added later do use enums here and there.)
  • hansvb
    hansvb over 13 years
    Interesting. Would public static final int BAR = new Random().nextInt() work?
  • Tony Ennis
    Tony Ennis over 13 years
    Thilo's statement compiles but the switch statement complains constant expression required. Further, couldn't two consecutive new Random().nextInt() return the same values?
  • hansvb
    hansvb over 13 years
    @Tony: Which is a good thing. It does not compile because it is not initialized with a compile-time constant. See Stephen's accepted answer. If that did compile, a random integer would be hard-coded into the class, with quite unpredictable results.
  • Tony Ennis
    Tony Ennis over 13 years
    I'm surprised the constant in the switch is rejected, and the 'constant' itself isn't. I never would of thought it would be this way. Of course, it isn't truly a constant I suppose.
  • Austin Hyde
    Austin Hyde about 9 years
    Just for clarification: This solves your error by making a static property final. In my original question, the issue was that the final static property was missing an initializer, making it a constant, but not a compile-time constant. See the accepted answer for details.
  • Teo Inke
    Teo Inke about 9 years
    I know it's a different problem, but since I got here with mine it could help someone else in the same situation.
  • everton
    everton almost 9 years
    @djangofan what JDK version are you running your code on?
  • djangofan
    djangofan almost 9 years
    I used JDK 1.7.0_74 with IntelliJ-IDEA 14
  • Amit Kumar
    Amit Kumar almost 9 years
    I am using same class as suggested by Everton Agner but its showing constant expression required.
  • slott
    slott over 8 years
    Makes sense they have to be final as things would go wrong if these values could change runtime.
  • shaolin
    shaolin over 8 years
    Hi, I'm still having the problem using the enum in this way: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } } <br/> When i try to use the enum in the switch I get the same error... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> It's possible to solve the problem?
  • Stephen C
    Stephen C about 8 years
    @stiga - You can only switch on the enum instances themselves. Not on some value returned by calling a method on the enum instances.
  • Stephen C
    Stephen C about 6 years
    @TonyEnnis - It depends on what you mean by truly constant. It is truly constant in the sense that it will not change during the execution of the program (modulo a couple of quibbles). But it is not the same for all executions.
  • J. Doe
    J. Doe almost 6 years
    This is a workaround and not an answer to the question
  • Samer Murad
    Samer Murad over 5 years
    Why do I keep getting down votes here? it's a legitimate workaround
  • Dean Wild
    Dean Wild over 4 years
    probably because it's an IF statement that we are specifically trying to avoid with a switch
  • Christian Lim
    Christian Lim over 4 years
    I down voted because the question here is not "how" to solve the problem, but "why" the problem occurred. I think your answer is out of context. Also if you're perfectionist, you should realize that switch is generally faster than long if-else, because switch only check the condition once, while with if-else you may need to check all condition before finding the right one.
  • user1364368
    user1364368 about 4 years
    I think the enum should have the following constructor: private Animal(String name) { this.name = name; }
  • PRANAV SINGH
    PRANAV SINGH over 3 years
    For more information regarding error in android studio refer this :-tools.android.com/tips/non-constant-fields
  • Samer Murad
    Samer Murad over 3 years
    @ChristianLim Fair enough, I agree this your statement, simply replacing a switch statement with an if statement should not be a goto solution and is definitely the wrong solution in some cases, and my answer does miss the "Why" part of the question. nevertheless in most simpler cases, it is still a valid workaround. But again, I totally agree, and I will update my answer to reflect these problematics