Java: using switch statement with enum under subclass

181,196

Solution 1

Change it to this:

switch (enumExample) {
    case VALUE_A: {
        //..
        break;
    }
}

The clue is in the error. You don't need to qualify case labels with the enum type, just its value.

Solution 2

Wrong:

case AnotherClass.MyEnum.VALUE_A

Right:

case VALUE_A:

Solution 3

Java infers automatically the type of the elements in case, so the labels must be unqualified.

int i;
switch(i) {
   case 5: // <- integer is expected
}
MyEnum e;
switch (e) {
   case VALUE_A: // <- an element of the enumeration is expected
}

Solution 4

this should do:

//Main Class
public class SomeClass {

    //Sub-Class
    public static class AnotherClass {
        public enum MyEnum {
            VALUE_A, VALUE_B
        }    
        public MyEnum myEnum;
    }

    public void someMethod() { 
        AnotherClass.MyEnum enumExample = AnotherClass.MyEnum.VALUE_A; //...

        switch (enumExample) {
            case VALUE_A: { //<-- error on this line
            //..
            break;
            }
        }
    }
}

Solution 5

From Java 14 onwards, one can use switch expressions.

For this post

public enum MyEnum {
    VALUE_A, VALUE_B;
}
public void someMethod() { 
    MyEnum enumExample //...

    switch (enumExample) {
        case VALUE_A -> {
            // logic
        }
        case VALUE_B -> {
            // logic
        }   
    }
}

Switch expression

Like all expressions, switch expressions evaluate to a single value and can be used in statements. They may contain "case L ->" labels that eliminate the need for break statements to prevent fall through. You can use a yield statement to specify the value of a switch expression.

public enum Month {
    JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC;
}

Example 1: Returns value.

public static int getNoOfDaysInAMonth(Month month, boolean isLeapYear) {
    return switch(month) {
        case APR, JUN, SEP, NOV -> 30;
        case FEB -> (isLeapYear)? 29: 28;
        case JAN, MAR, MAY, JUL, AUG, OCT, DEC -> 31;
    };
}

Example 2: Doesn't returns value.

public static void printNoOfDaysInAMonth(Month month, boolean isLeapYear) {
    switch(month) {
        case APR, JUN, SEP, NOV -> {
            System.out.println("30 days");
        }
        case FEB -> {
            System.out.println(((isLeapYear)? 29: 28) + " days");
        }
        case JAN, MAR, MAY, JUL, AUG, OCT, DEC -> {
            System.out.println("31 days");
        }
    };
}

Reference

Switch Expressions

Share:
181,196
Popokoko
Author by

Popokoko

Updated on July 08, 2022

Comments

  • Popokoko
    Popokoko almost 2 years

    First I'll state that I'm much more familiar with enums in C# and it seems like enums in java is a quite mess.

    As you can see, I'm trying to use a switch statement @ enums in my next example but I always get an error no matter what I'm doing.

    The error I receive is:

    The qualified case label SomeClass.AnotherClass.MyEnum.VALUE_A must be replaced with the unqualified enum constant VALUE_A

    The thing is I quite understand the error but I can't just write the VALUE_A since the enum is located in another sub-class. Is there a way to solve this problem? And why is it happening in Java?

    //Main Class
    public class SomeClass {
    
        //Sub-Class
        public static class AnotherClass {
            public enum MyEnum {
                VALUE_A, VALUE_B
            }    
            public MyEnum myEnum;
        }
    
        public void someMethod() { 
            MyEnum enumExample //...
    
            switch (enumExample) {
                case AnotherClass.MyEnum.VALUE_A: { <-- error on this line
                    //..
                    break;
                }
            }
        }
    }
    
  • Popokoko
    Popokoko about 12 years
    Ok i feel so stupid :-( You are totally right, i was convinced i tried this exact line and got an error with that so i moved to qualify case, but your suggestion DOES work.
  • darrengorman
    darrengorman about 12 years
    By the way I think you'll find that enums in Java are incredibly useful once you start to use them more, I wouldn't say they're a mess at all :)
  • Popokoko
    Popokoko about 12 years
    I believe i will find you are right in the close future :) Anyway, this is definitely the best programming forum around thanks to people like you.
  • jjz
    jjz over 10 years
    @milkplusvellocet, I know this post is already old, but I'm curious why Java don't allow the qualified case label in the switch statement?
  • darrengorman
    darrengorman over 10 years
    @cRane01 don't know for sure, but it makes for a cleaner syntax. Specifying the type on each case would be totally redundant
  • HelloGoodbye
    HelloGoodbye almost 10 years
    What about if you have another constant called the same thing in another class? Wouldn't VALUE_A be ambiguous in that case?
  • matbrgz
    matbrgz almost 9 years
    Why must it be unqualified?
  • Kru
    Kru almost 9 years
    If you could qualify, then you might use something else than MyEnum which would not make sense.
  • sprinter
    sprinter over 8 years
    @HelloGoodbye No. The switch statement's variable defines the type of the case statement so it can only be one enum.
  • HelloGoodbye
    HelloGoodbye over 8 years
    @sprinter Ah, I see. Well that's clever.
  • JakeTheSnake
    JakeTheSnake over 7 years
    Question: Let's say I have: enum AnimalType { Fish, Horse }. I have a parameter AnimalType a in a function with the statement: switch(myAnimal.getType()) and then case a in that. I get the error, what to do?
  • Sasha
    Sasha over 6 years
    @Kru, but I can use something grammatically-else for non-enum-typed case expressions. E.g. static final int MY_CONST = 7; …; switch(intVariable) {case MY_CONST: …;} instead of case 7. So this restriction for enums makes no sense (I can use not only primary literals, but also manually-defined constants for integer switch expression, but I can't use manually-defined constants, but only primary names for enums).
  • Soham Mehta
    Soham Mehta about 5 years
    You saved the day!
  • joaorodr84
    joaorodr84 over 4 years
    I upvoted yours because it is clearer on what the issue is.
  • BAERUS
    BAERUS over 3 years
    Upvoted this answer already back in 2015 .. here we go again :D Still don't understand why a qualified name is a compiler error. Like, if it's ugly and redundant, so be it, that's on my shoulders. My IDE can tell me that it's unnecessary, but crashing the compiling? I don't know ... Seems odd to me at least, and therefore unintuitive.
  • Sridhar Sarnobat
    Sridhar Sarnobat over 3 years
    This is slightly counter-intuitive but solved my issue too. When you have a slow build for a large system (made worse by gradle) and have to upgrade a library due to a CVE security flaw urgently, this compile error costs me a lot of valuable time.
  • user13947194
    user13947194 almost 3 years
    @Sasha enums in Java don't have integers assigned to them like in C++. They are abstract just like java boolean.
  • Sasha
    Sasha over 2 years
    @user13947194, nobody said they have. I just pointed out the logical fallacy: for one type we can use both the primary literals and the manually-defined constants while for the other type we can use only primary names but not manually-defined constants.
  • user13947194
    user13947194 over 2 years
    @Sasha I don't understand what you are talking about. Java enums are not associated with an integer and can't be associated with an integer. They can only be referenced by their name.
  • Sasha
    Sasha over 2 years
    @user13947194, “They [enum values] can only be referenced by their name” — nope. We can create aliases for enum values — static final MyEnumType aliasToEnumValue = MyEnumType.VALUE_A; as well as we can create aliases for integer values — static final int aliasToIntValue = 12. But the design flaw is that only the latter (case aliasToIntValue) can be used a case label but not the former (case aliasToEnumValue). (As for “Java enums are not associated with an integer and can't be associated with an integer” — please don't argue with the statements that I have never expressed.)
  • user13947194
    user13947194 over 2 years
    @Sasha now I understand you. I think enums in Java are Objects, and when you apply final to Objects you only have a const pointer and not a const Object. int on the other hand is a primitive. Applying final to int makes the value itself const. Another thing is, you probably dont need to do this, and Java is the type of parent to push you in the "right" direction.
  • Sasha
    Sasha over 2 years
    @user13947194, "when you apply final to Objects you only have a const pointer and not a const Object" — that doesn't matter whether the object itself is const or not, because: (1) const final myAlias = MyEnumType.VALUE_E copies only the pointer; (2) switch (myEnumValue) {…} compares only the pointer. So fields in MyEnumType (if there are any) are irrelevant here. So, handling of enums and ints/chars is identical here (but syntax is different and that's weird). "you probably dont need". Of course, I need.
  • Sasha
    Sasha over 2 years
    My philosophy is: every logical inconsistency (including syntactic ones) may bite, sooner or later. Even if a specific one haven't bitten somebody yet, that doesn't mean it will never do that.
  • user13947194
    user13947194 over 2 years
    @Sasha I don't know how you reckon that switch(myEnumValue) compares only the pointer. If that were the case, two enum objects of the same value would not compare equal.
  • Sasha
    Sasha over 2 years
    @user13947194, can you please provide an example of what you mean by "two enum objects of the same value"?
  • user13947194
    user13947194 over 2 years
    @Sasha I did a little research and found that you can't create multiple instances of an enum using new keyword. Which means only one instance of an enum is created. One might then think we can switch on pointers. But sadly you cant do that because pointers are only known at runtime. To emphasize the point, switch statements only work on values known at compile time. To emphasize further, since you only and can only make the pointer const; Java has no way of knowing what value is stored in your enum variable. You can change that value at anytime, during runtime.
  • Sasha
    Sasha over 2 years
    @user13947194… ([0] “you can't create multiple instances of an enum using new keyword” — of course.) So, I've checked the generated bytecode. [1] You were right that, in case of enums, Java's switch doesn't simply compare pointers (although I disagree that pointer is always unknown at compile time; it's possible to know in some cases; but Java doesn't use that). [2] You were wrong that it checks the values of the fields; instead, it checks the result of the call to the enum's predefined virtual method; that's the thing a user can't influence (regardless to whether fields are const or no).
  • Sasha
    Sasha over 2 years
    @user13947194, so, in general, I oppose to your assumption that inability to mark an object value as const somehow relates to the restriction of using enum aliases as switch case labels.
  • user13947194
    user13947194 over 2 years
    @Sasha You mind telling me how pointers can be known at compile time? Whether it checks a value or a value obtained from a method is not the point. The point is you can change the value of your enum variable at anytime via assigning it a different value. MyEnum y = MyEnum.a; y = MyEnum.z; If you further 'oppose' my 'assumption' I can't make a better case.
  • Sasha
    Sasha over 2 years
    @user13947194, "you can change the value of your enum variable at anytime via assigning it a different value". You can't. In MyEnum y = MyEnum.a; y = MyEnum.z, exactly the "pointer" gets changed (not the "object" itself). And when MyEnum y is marked as final, changing the "pointer" is forbidden. (As for the pointers at compile time, it's off-topic, because it's not used here anyway; and I was partially wrong — not the pointers themselves but their offsets to a certain base can be theoretically known at compile time (which should be still enough); but that's not used anyway here.)
  • user13947194
    user13947194 over 2 years
    @Sasha you make a tough point. I must say I never ever create Java enums and always used static final ints. Yes you can't modify a final enum pointer; essentially making it truly immutable. Now all we need to do is compare the offsets; and we know them at compile time! The only thing I can think of is, are these offsets relative to the entire app?
  • Sasha
    Sasha over 2 years
    @user13947194, sorry for not replying, there was a rush at work. I suppose, these offsets are per module (i.e. per *.dll- or *.so-file in the native world and per *.jar file in JRE world). Because every module may expect that all of its “static” data is put into contiguous area by the “linker” but has no clue about other modules' “static” data. But that would be enough, as all values for a specific enum are always declared in the same module (Java doesn't support extending enums).
  • Sasha
    Sasha over 2 years
    Nevertheless, I think this is offtopic, as (1) Java uses virtual method calls (not pointer offsets) when comparing enums in switch and (2) as well as the pointer offsets strategy, the current strategy (based on virtual method calls) doesn't in any way prevent from using enum aliases in switches. So, to be short, the only thing that prevents us from using enum aliases in switches in Java is the syntax (technical reasons don't prevent it). Do you agree now with me that such syntax is a design flaw?
  • user13947194
    user13947194 over 2 years
    I am happy that you are busy at work. I am busy coding too, but self study. Now your first logic/ point is that since the entire enum is stored in one JAR file; Java would be able to deduce via offset. There is a problem sadly. A Java object can exist in multiple class/JAR files. Infact a java object can be received over a network. The short story, you can't rely on offsets to deduce a Java Object. Other possible problem is the assumption that Java can manipulate offsets to deduce an object. Once an object is loaded into memory it is now an OS specific environment.
  • user13947194
    user13947194 over 2 years
    Your second logic is that your logic one is off topic aka irrelevant to our discussion; as Java uses virtual method calls to get enum value. This shows me you didn't learn from my previous elaborations. You cannot switch on method calls as methods calls are not integer values. You cannot switch on pointers because pointers are only known at runtime.
  • Sasha
    Sasha over 2 years
    @user13947194, yes, let's really stop discussion of pointers, because they are really offtopic (despite I believe that you're not 100% correct about them). Method calls aren't integer values but they return integer values. To be precise, Java uses virtual method call to determine the integer value associated with the result of an enum-typed expression in parentheses (after switch) while the integer values associated with the enum values used as case labels are known at compile-time. (That's how it works de-facto, you can check that with javap.)
  • Sasha
    Sasha over 2 years
    (BTW, I need to say sorry, because some time ago I said “I never stated that enums have associated ints”, but now I do state that. Because at that point of time I didn't care about actual implementation and didn't look into javap and then I did.) And, to say truth, now I don't understand your point at all: IIUC, you're saying that switch for enums in Java can be implemented neither by comparing pointers (or offsets) nor by calling a virtual method; but the fact is that switch for enums is impemented in Java in some way; does that mean that you assume some third way?
  • user13947194
    user13947194 over 2 years
    I am happy you finally get that you can't switch on a method; you must switch on the integer it returns. Question: what do you know about polymorphic/ virtual methods. If nothing else you must know that virtual methods are runtime features. Cannot be deduced at compile time. So even if Java doesn't use virtual pointer tables to implement virtual methods; it makes no difference as they are still runtime information. And switch statements require that they be known at compile time.