Switch Expressions

Quick Summary:

  • Revamps the switch-statement, modernising and simplifying it.
  • No more break-statements!
  • Also, we’re using arrow operators “->” now!
  • We can even use switch as an expression (meaning it can evaluate to a value to be assigned or returned),
  • Released in Java 14 (2020), but introduced in Java 12 (2017).

Key Features

The introduction of switch expressions brings new syntax and keywords that streamline coding practices:

FeatureDescriptionBenefit
Arrow (->)Directs the output for each case without requiring break statements.Eliminates fall-through errors, simplifying control flow.
Multiple case labelsMultiple case labels can be combined using commas to share a single output.Reduces code redundancy, making it cleaner and more concise.
yield KeywordUsed to return a value from a block of code within a switch case.Facilitates returning complex values from multi-statement cases.
Default caseUsed as a fallback if no other case matches, now mandatory in switch expressions.Ensures all scenarios are covered, improving code robustness.

Example

Before Java 17:

With classic switch-statements, we had to be extra aware of using the break-statement correctly, and fall-through logic could get quite verbose..

    public static void main(String[] args) {
        String day = "Saturday";
        switch (day) {
            case "Monday":
            case "Tuesday":
            case "Wednesday":
            case "Thursday":
            case "Friday":
                System.out.println("It's a work day");
                break;
            case "Saturday":
            case "Sunday":
                System.out.println("It's weekend");
                break;
            default:
                throw new IllegalArgumentException("Invalid day: " + day);
        }
    }

After Java 17:

With the new syntax, we can use multiple case labels at a time to keep our code concise, and we don’t need to worry about accidentally forgetting a break-statement as the arrow syntax takes care of that for us.

public class DaysOfWeekAfterDemo {
    public static void main(String[] args) {
        String day = "Saturday";

        switch (day) {
            case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> System.out.println("It's a work day");
            case "Saturday", "Sunday" -> System.out.println("It's weekend");
            default -> throw new IllegalArgumentException("Invalid day: " + day);
        }
    }
}

Switch-expressions to “return” a value!

It gets even cooler when you realise we can use switch-expressions now:

    public static void main(String[] args) {
        String day = "Saturday";
        String message = switch (day) {
            case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "It's a work day";
            case "Saturday", "Sunday" -> "It's weekend";
            default -> throw new IllegalArgumentException("Invalid day: " + day);
        };
        System.out.println(message);
    }

Being able to assign the result to a variable is quite a powerful, functional way of programming that can help us keep our code much more concise and less repetitive.

The yield Keyword

  • But what about this “yield” keyword?
  • You know how when you use a switch-expression, you can think of it as if the switch-construct is “returning” a value, allowing us to assign it to a variable, for example?
  • Well, technically that’s not actually a proper return (duh, that’s for methods), but a yield.
  • In the example above, we are in fact yielding “It’s a work day” or “It’s weekend”, but because it’s just a one-liner, Java lets us omit it for brevity (but it’s there implicitly!).
  • If your case-logic is more than one line of code, then you can’t omit the curly braces or the yield keyword.
  • Our previous example would look like this:
    public static void main(String[] args) {
        String day = "Saturday";
        String message = switch (day) {
            case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> {
                // More lines of code here
                yield "It's a work day";
            }
            case "Saturday", "Sunday" -> {
                // More lines of code here
                yield "It's weekend";
            }
            default -> throw new IllegalArgumentException("Invalid day: " + day);
        };
        System.out.println(message);
    }

So yield acts as a kind of mini-return, but for switch-expressions, and it’s not even necessary to explicitly use it if your case-logic is a one-liner.

The New switch: How to Write it

Switch expressions introduce a new syntax that is more concise and safer than the traditional switch statement. Here’s how to write a switch expression step-by-step:

  1. Start with the switch keyword, followed by the variable you are switching on enclosed in parentheses, then use curly braces to enclose a scope.
  2. Per case, use the case keyword, followed by the label you want this case-logic to be associated with.
  3. Optionally, combine multiple labels, separating them with commas if they should result in the same output.
  4. Use the arrow operator (->) after each case label to signify what should happen for that label. This operator eliminates the need for break statements.
  5. Optionally Include a yield keyword within a block if the case requires multiple statements or complex logic to determine the return value. Note: This is only relevant for switch-expressions, not switch-statements.
  6. Use a default case to define what happens if none of the specified cases match the input.

👩‍💻 Hands-on Demo: Switch Expressions

🔎 Click here to expand

1. Multiple Choice Quiz

What is the primary advantage of using the arrow (->) syntax in switch expressions introduced in Java 14?

A) It allows for fall-through behavior.
B) It eliminates the need for break statements.
C) It supports multiple switch cases.
D) It makes the switch expression faster.

2. Fill-in-the-Blanks

  • Complete the code snippet by filling in the blanks to correctly implement a switch expression in Java that returns the number of days in a month.
public class DaysInMonth {
    public static _____ getDays(int month, boolean isLeapYear) {
        _______ switch (month) {
            _______ 1, 3, 5, 7, 8, 10, 12 -> 31;
            _______ 4, 6, 9, 11 -> 30;
            _______ 2 __ isLeapYear ? __ : __;
            _______ -> throw new IllegalArgumentException("Invalid month: " + month);
        };
    }

    public static void main(String[] args) {
        int days = getDays(2, true);
        System.out.println("Days: " + days);
    }
}

3. Debugging Challenge: Code Correction

Identify and correct the errors in the following Java code that is supposed to use a switch expression to output the type of website based on URL.

public class WebsiteType {
    public static String getWebsiteType(String url) {
        return switch (url) {
            case url.contains("edu") -> "Educational";
            case url.contains("org") -> "Organization";
            case url.contains("com") -> "Commercial";
            case url.contains("net") -> "Network";
            default -> "Unknown";
        };
    }

    public static void main(String[] args) {
        String type = getWebsiteType("http://www.example.com");
        System.out.println("Website Type: " + type);
    }
}

4. Grading System

In this exercise, you will work with a simple grading system that categorizes scores into grades (A, B, C, D, F), first with a classic switch-statement, then with the new syntax!

  1. In a new GradingApp-class, ask the user to enter a grade (A, B, C, D or F).
  2. Depending on the value of the input grade, the user gets different feedback (e.g. “That’s excellent!”, “Good job!”, “Not bad…”, “Oof! Better luck next time!” and “An F!? Better get to studying!”).
    • Use a classic switch-statement to achieve this.
    • An invalid grade should throw an exception.
  3. Refactor it by using the new syntax.
  4. Refactor it again by turning it into a switch-expression.

5. Customer Support Routing

Route helpdesk tickets based on the nature of the query. Tickets related to “software”, “hardware”, or “network” might route to different departments but some might fall through to a default support group.

  1. In a new HelpDeskApp-class, ask the user to enter a query category depending on their issue (i.e. “software”, “hardware”, “network”, etc.).
  2. Write a routeTicket-method that accepts a query category and returns the name of the department (with phone number).
    • Use a switch-expression to make this a one-liner return statement.
    • Some query categories fall under the same department, so use multi-label cases to solve that.
    • For the “software” case, make sure a makeCoffee()-method is called before yielding the name of the department (you’ll have to use curly braces and the yield-keyword to solve this).
  1. Back in the main-method, after asking the user to input a query category, pass this to the routeTicket-method to obtain the department name and phone number, and print it.

Solutions

🕵️‍♂️ Click here to reveal the solutions

1. Multiple Choice Quiz

Answer: B) It eliminates the need for break statements.

2. Fill-in-the-Blanks

public class DaysInMonth {
    public static _____ getDays(int month, boolean isLeapYear) {
        _______ switch (month) {
            _______ 1, 3, 5, 7, 8, 10, 12 -> 31;
            _______ 4, 6, 9, 11 -> 30;
            _______ 2 __ isLeapYear ? __ : __;
            _______ -> throw new IllegalArgumentException("Invalid month: " + month);
        };
    }

    public static void main(String[] args) {
        int days = getDays(2, true);
        System.out.println("Days: " + days);
    }
}
public class DaysInMonth {
    public static int getDays(int month, boolean isLeapYear) {
        return switch (month) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> isLeapYear ? 29 : 28;
            default -> throw new IllegalArgumentException("Invalid month: " + month);
        };
    }

    public static void main(String[] args) {
        int days = getDays(2, true);
        System.out.println("Days: " + days);
    }
}

3. Debugging Challenge: Code Correction

Original:
public class WebsiteType {
    public static String getWebsiteType(String url) {
        if (url.contains("edu")) {
            return "Educational";
        } else if (url.contains("org")) {
            return "Organization";
        } else if (url.contains("com")) {
            return "Commercial";
        } else if (url.contains("net")) {
            return "Network";
        } else {
            return "Unknown";
        }
    }

    public static void main(String[] args) {
        String type = getWebsiteType("http://www.example.com");
        System.out.println("Website Type: " + type);
    }
}
Corrected (classic if-else tree):
public class WebsiteType {
    public static String getWebsiteType(String url) {
        if (url.contains("edu")) {
            return "Educational";
        } else if (url.contains("org")) {
            return "Organization";
        } else if (url.contains("com")) {
            return "Commercial";
        } else if (url.contains("net")) {
            return "Network";
        } else {
            return "Unknown";
        }
    }

    public static void main(String[] args) {
        String type = getWebsiteType("http://www.example.com");
        System.out.println("Website Type: " + type);
    }
}
Explanation:
  • The original code will not compile because case labels in a switch expression cannot use method calls like contains.
  • Instead, use url in a if-else structure or modify the design to use a pattern matching approach in Java 21 and later versions (see below).
Corrected (JDK 21+ feature Pattern Matching for switch case):
public class WebsiteType {
    public static String getWebsiteType(String url) {
        return switch (url) {
            case String s when s.contains("edu") -> "Educational";
            case String s when s.contains("org") -> "Organization";
            case String s when s.contains("com") -> "Commercial";
            case String s when s.contains("net") -> "Network";
            default -> "Unknown";
        };
    }

    public static void main(String[] args) {
        String type = getWebsiteType("http://www.example.com");
        System.out.println("Website Type: " + type);
    }
}

4. Grading System

public class GradingApp {
    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        System.out.print("Enter your grade (A, B, C, D or F): ");
        String inputGrade = keyboard.nextLine();

        // Classic switch statement
        switch (inputGrade) {
            case "A":
                System.out.println("That's excellent!");
                break;
            case "B":
                System.out.println("Good job!");
                break;
            case "C":
                System.out.println("Not bad...");
                break;
            case "D":
                System.out.println("Oof! Better luck next time!");
                break;
            case "F":
                System.out.println("An F!? Better get to studying!");
                break;
            default:
                throw new IllegalArgumentException("Invalid grade: " + inputGrade);
        }

        // Refactored modern switch statement
        switch (inputGrade) {
            case "A" -> System.out.println("That's excellent!");
            case "B" -> System.out.println("Good job!");
            case "C" -> System.out.println("Not bad...");
            case "D" -> System.out.println("Oof! Better luck next time!");
            case "F" -> System.out.println("An F!? Better get to studying!");
            default -> throw new IllegalArgumentException("Invalid grade: " + inputGrade);
        }

        // Refactored switch expression
        String message = switch (inputGrade) {
            case "A" -> "That's excellent!";
            case "B" -> "Good job!";
            case "C" -> "Not bad...";
            case "D" -> "Oof! Better luck next time!";
            case "F" -> "An F!? Better get to studying!";
            default -> throw new IllegalArgumentException("Invalid grade: " + inputGrade);
        };
        System.out.println(message);
    }
}

5. Customer Support Routing

public class HelpDeskApp {
    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        System.out.print("Enter your query category (Software, Hardware, etc.): ");
        String inputQueryCategory = keyboard.nextLine();
        String department = routeTicket(inputQueryCategory.toLowerCase());
        System.out.println(department);
    }

    private static String routeTicket(String queryCategory) {
        return switch (queryCategory) {
            case "software" -> {
                makeCoffee(3);
                yield "Software Support Team | +32 400 000 121";
            }
            case "hardware" -> "Hardware Support Team | +32 400 000 122";
            case "network",
                 "internet", "connectivity" -> "Network Support Team | +32 400 000 123";
            default -> "General Support Team | +32 400 000 000";
        };
    }

    private static void makeCoffee(int cups) {
        System.out.printf("Making %d cups of coffee...\n", cups);
    }
}

Summary

  • Simplified Syntax: Java’s updated switch statements no longer require break statements, thanks to the new -> arrow operator, which prevents errors and simplifies the code.
  • Expression Capabilities: Switches can now directly return values, allowing you to assign the result to a variable, making your code cleaner and more straightforward.
  • Multiple Cases, One Action: Combine several conditions with commas in a single case to handle multiple scenarios simultaneously, reducing redundancy.
  • Yield for Many-Liners: Use yield to return values from complex case scenarios within switch expressions, enhancing clarity and control.
  • Mandatory Default Case: Including a default case ensures that all possibilities are covered, improving your program’s reliability.