Java’s Switch Statement in Three Minutes

    Nicolai Parlog
    Share

    Java’s switch statement allows the comfortable selection of one of many execution paths based on a variable’s value. The variable must either be an enum, a String, or an integral type like int. Given to switch, it is compared with a list of case labels that each specify a value – as soon as the first one matches, the corresponding statement block is executed. The switch statement works much like a long ifelseif chain and can often be used to replace it.

    This article only requires working knowledge of integers and strings but the more you know about if and particularly ifelseif the more you will get out of it. Experience with other numeric types, enums and methods are a bonus.

    Using switch

    Let’s jump right in and start with an example! The following switch statement writes a textual representation of the first three natural numbers to the console:

    // any number is fine
    int number = 2;
    switch (number) {
        case 0:
            System.out.println("zero");
            break;
        case 1:
            System.out.println("one");
            break;
        case 2:
            System.out.println("two");
            break;
        default:
            System.out.println("many");
            break;
    }
    

    Just from looking at it, what do you think will happen?

    The switch will look at number, which is currently 2, and compare it with each of the values behind the case keywords. It’s not 0, it’s not 1, it’s 2. Bingo! So off it goes calling System.out.println("two").

    Syntax of switch

    You can use the switch statement with variables of type int, byte, short, char (note that long does not work), String, or an enum (or enumeration type as they are formally called). Here’s how it works:

    switch (<variable>) {
        case <value>:
            // statements
            break;
        case <other-value>:
            // statements
            break;
        case <more-values>:
            // statements
            break;
        default:
            // statements
            break;
    }
    

    You use the keyword switch followed by the variable you want to switch over (as it is commonly phrased) and a pair of curly braces. Inside those curly braces, you list as many branches as you like.

    Each regular branch consists of the keyword case, a value that matches the variable’s type (meaning it could be assigned to it), and a colon. Together, these three things are called a switch label. It is followed by the statements you want to execute if the variable has that particular value. Unless you have a very good reason, every switch-branch should end in a break. (I’ll explain in a minute, why.)

    If you need a branch that is executed if none of the labels matched, you can create one with default. It works much like a regular branch but takes no value.

    Fall-Through

    It is important to note that the switch statement does not only execute the matching branch. Instead it starts executing code at the matching label. If it does not hit a break it will go right on into the next branch. This is called fall through and can be used intentionally.

    The following statement starts counting at the given number:

    // any number is fine
    int number = 1;
    switch (number) {
        case 0:
            System.out.println("zero");
            // fall-through intended
        case 1:
            System.out.println("one");
            // fall-through intended
        case 2:
            System.out.println("two");
            // fall-through intended
        default:
            System.out.println("many");
    }
    

    In this case, the output is one two many because number matches 1 and switch executes all blocks because there are no breaks. The comment “fall-through intended” tells your fellow programmers that you did this on purpose and not just forgot to add the break.

    Return Inside switch

    Many developers consider repeating break ugly and try to push switches into methods, so that they can return a value. Because a return ends execution of that method, there is no risk to fall through and hence no need for break.

    For the first example, the code might look as follows:

    public String toEnglish(int number) {
        switch (number) {
            case 0: return "zero";
            case 1: return "one";
            case 2: return "two";
            default: return "many";
        }
    }
    

    Much shorter, isn’t it? Executing System.out.println(toEnglish(2)) would then print two.

    Other Types

    I already mentioned that you can switch over more than just ints. Other primitives that allow that are byte, short, and char. You can also switch over they wrapper types Integer, Byte, Short, and Character but note that you will get a NullPointerException if the variable is null.

    You can also switch over String

    public int toNumber(String number) {
        switch (number) {
            case "zero": return 0;
            case "one": return 1;
            case "two": return 2;
            default: throw new IllegalArgumentException();
        }
    }
    

    … and enums.

    public enum LittleNumber { ZERO, ONE, TWO }
    
    public int toNumber(LittleNumber number) {
        switch (variable) {
            case ZERO: return "zero (0)";
            case ONE: return "one (1)";
            case TWO: return "two (2)";
            default: return "unknown";
        }
    }
    

    Summary

    You learned how the switch statement is put together with the switch keyword, labels (case, a value, and a colon or default and a colon) and code blocks. On the first match, the code is executed until it hits a break, which might mean that it falls through into following branches. To keep switches short, it is common to put them into methods and return the results, thus avoiding breaks.

    Armed with this knowledge, you can dive deeper. If you wonder why you can’t use long, give this StackOverflow question a go. Also, you might soon wish there were ways to give the case branches a little more expressiveness, for example by checking whether a numerical value is smaller than another. Then you’re thinking towards pattern matching, a feature Java does not yet support directly but may soon. Until then, library implementations can be a replacement and Javaslang has a good one.

    CSS Master, 3rd Edition