{"id":"switch-and-text-switch-expressions","slug":"switch-expressions","title":"switch expressions and arrows","text":"Since Java 14, a case can be written with an arrow, `->`, in place of the colon. An arrow case runs only what's after its arrow, and never falls through, so it needs no `break`:\n\n```java\nint button = 2;\nswitch (button) {\n    case 1 -> System.out.println(\"Chips\");\n    case 2 -> System.out.println(\"Chocolate bar\");\n    case 4, 5 -> System.out.println(\"Water\");\n    default -> System.out.println(\"No such button\");\n}\n```\n\n```output\nChocolate bar\n```\n\nAfter an arrow comes one statement, or a block in braces for several. Several values share a case with commas, `case 4, 5 ->`. One switch uses either colons or arrows, not both.\n\n### A switch that gives a value\n\nMost switches choose a value: the name of a snack, a price, a number of points. Since Java 14, a switch can be an **expression**, which gives a value like `a + b` does. The value of each case is what comes after its arrow:\n\n```java\nint button = 4;\nString snack = switch (button) {\n    case 1 -> \"Chips\";\n    case 2 -> \"Chocolate bar\";\n    case 3 -> \"Apple\";\n    case 4, 5 -> \"Water\";\n    default -> \"nothing\";\n};\nSystem.out.println(\"You get: \" + snack);\n```\n\n```output\nYou get: Water\n```\n\n- The whole `switch (...) { ... }` is the value given to `snack`, so the statement ends with a semicolon after the closing brace.\n- Every case gives a value of the same kind, here a String.\n- A switch expression can be used wherever a value can: assigned to a variable, printed, or returned, as in `return switch (button) { ... };`.\n\n### Every value needs a case\n\nA variable must get a value, so a switch expression must have a case for every value its selector can have. For an int, that means a `default`:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        int button = 4;\n        String snack = switch (button) {\n            case 1 -> \"Chips\";\n            case 2 -> \"Chocolate bar\";\n        };\n        System.out.println(snack);\n    }\n}\n```\n\n```javac\nMain.java:4: error: the switch expression does not cover all possible input values\n        String snack = switch (button) {\n                       ^\n1 error\n```\n\nA switch *statement* with arrows, like the first example of this step, has no such rule: a value without a case simply does nothing.\n\n### Several statements: yield\n\nWhen a case needs a few statements to work out its value, the arrow is followed by a block, and `yield` gives the block's value. Buttons 6 to 9 are sandwiches, which cost more on the weekend:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(price(2, false));\n        System.out.println(price(7, false));\n        System.out.println(price(7, true));\n    }\n\n    public static int price(int button, boolean weekend) {\n        return switch (button) {\n            case 1, 2, 3 -> 150;\n            case 4, 5 -> 120;\n            case 6, 7, 8, 9 -> {\n                int base = 350;\n                if (weekend) {\n                    base = base + 50;\n                }\n                yield base;\n            }\n            default -> 0;\n        };\n    }\n}\n```\n\n```output\n150\n350\n400\n```\n\n`yield base;` ends the block, and `base` becomes the value of the switch. It's `yield`, not `return`: a `return` would try to leave the whole method, and javac doesn't allow it inside a switch expression. A block that reaches its end without a `yield` doesn't compile either: javac says \"switch rule completes without providing a value\".\n","task":"A shopping mall's directory sign shows what is on each level. The program reads a floor's number, a whole number on one line, and prints its place, with a chain of `if` and `else if`. Rewrite it so that a switch expression with arrows gives the place, and the program prints it. Keep what it prints the same: `Parking` for -1, `Lobby` for 0, `Shops` for 1, `Food court` for 2, and `Offices` for any other floor. With the input `2`, the program prints:\n\n```\nFood court\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int floor = Integer.valueOf(scanner.nextLine());\n        String place;\n        if (floor == -1) {\n            place = \"Parking\";\n        } else if (floor == 0) {\n            place = \"Lobby\";\n        } else if (floor == 1) {\n            place = \"Shops\";\n        } else if (floor == 2) {\n            place = \"Food court\";\n        } else {\n            place = \"Offices\";\n        }\n        System.out.println(place);\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int floor = Integer.valueOf(scanner.nextLine());\n        String place = switch (floor) {\n            case -1 -> \"Parking\";\n            case 0 -> \"Lobby\";\n            case 1 -> \"Shops\";\n            case 2 -> \"Food court\";\n            default -> \"Offices\";\n        };\n        System.out.println(place);\n    }\n}\n","hints":["Start with `String place = switch (floor) {` and give each floor a line such as `case 0 -> \"Lobby\";`.","The `else` becomes `default -> \"Offices\";`. Remember the semicolon after the switch's closing brace: the whole switch is the value of `place`."],"tests":[{"name":"Test 1","stdin":"2\n","expect":"Food court","hidden":false},{"name":"Test 2","stdin":"-1\n","expect":"Parking","hidden":true},{"name":"Test 3","stdin":"0\n","expect":"Lobby","hidden":true},{"name":"Test 4","stdin":"1\n","expect":"Shops","hidden":true},{"name":"Test 5","stdin":"14\n","expect":"Offices","hidden":true},{"name":"Test 6","stdin":"-2\n","expect":"Offices","hidden":true}],"require":[{"pattern":"(=|return|\\()\\s*switch\\s*\\(","message":"Get the place from a switch expression, such as `String place = switch (floor) { ... };`."},{"pattern":"->","message":"Write the cases with arrows, such as `case 0 -> \"Lobby\";`."}],"forbid":[{"pattern":"\\bif\\s*\\(","message":"Replace the `if` chain with the switch."}],"more":[{"task":"A calendar needs the number of days in each month of a year that isn't a leap year. Write the method `daysInMonth(int month)`, which returns the number of days in the month (1 is January, 12 is December) with a switch expression with arrows: 31 for months 1, 3, 5, 7, 8, 10 and 12, 30 for months 4, 6, 9 and 11, and 28 for month 2. For any other number it returns 0. Give each number of days one case, with several values per case. The check runs `System.out.println(daysInMonth(4));`, which prints:\n\n```\n30\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(daysInMonth(4));\n    }\n\n    public static int daysInMonth(int month) {\n        return 0;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(daysInMonth(4));\n    }\n\n    public static int daysInMonth(int month) {\n        return switch (month) {\n            case 1, 3, 5, 7, 8, 10, 12 -> 31;\n            case 4, 6, 9, 11 -> 30;\n            case 2 -> 28;\n            default -> 0;\n        };\n    }\n}\n","hints":["A case can list several values with commas: `case 4, 6, 9, 11 -> 30;`.","Return the switch's value straight away: `return switch (month) { ... };`, with `default -> 0;` as the last case."],"tests":[{"name":"System.out.println(daysInMonth(4));","stdin":"","call":"System.out.println(daysInMonth(4));\n","expect":"30","hidden":false},{"name":"System.out.println(daysInMonth(1) + \" \" + daysInMonth(2) + \" \" + daysInMonth(3));","stdin":"","call":"System.out.println(daysInMonth(1) + \" \" + daysInMonth(2) + \" \" + daysInMonth(3));\n","expect":"31 28 31","hidden":true},{"name":"System.out.println(daysInMonth(7) + \" \" + daysInMonth(8) + \" \" + daysInMonth(9));","stdin":"","call":"System.out.println(daysInMonth(7) + \" \" + daysInMonth(8) + \" \" + daysInMonth(9));\n","expect":"31 31 30","hidden":true},{"name":"System.out.println(daysInMonth(11) + \" \" + daysInMonth(12) + \" \" + daysInMonth(13) + \" \" + daysInMonth(0));","stdin":"","call":"System.out.println(daysInMonth(11) + \" \" + daysInMonth(12) + \" \" + daysInMonth(13) + \" \" + daysInMonth(0));\n","expect":"30 31 0 0","hidden":true}],"require":[{"pattern":"\\bswitch\\s*\\(\\s*month\\s*\\)","message":"Choose the number with `switch (month)`."},{"pattern":"\\bcase\\s+\\d+\\s*,\\s*\\d+","message":"List the months that have the same number of days in one case, such as `case 4, 6, 9, 11 -> 30;`."},{"pattern":"->","message":"Write the cases with arrows."}],"forbid":[{"pattern":"\\bif\\s*\\(","message":"Use the switch instead of `if`."}]},{"task":"A parcel service charges by zone, in cents. Write the method `postage(int zone, int grams)` with a switch expression on the zone:\n\n- zone 1 (the same town): 150, whatever the weight\n- zone 2 (the same country): 300, whatever the weight\n- zone 3 (abroad): 800, plus 50 for each full 100 grams over 500 grams. A parcel of 1250 grams is 750 grams over, which holds seven full hundreds, so it costs 800 + 7 * 50 = 1150.\n- any other zone: -1\n\nWork out zone 3's price in a block that ends with `yield`. The check runs `System.out.println(postage(3, 1250));`, which prints:\n\n```\n1150\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(postage(3, 1250));\n    }\n\n    public static int postage(int zone, int grams) {\n        return 0;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(postage(3, 1250));\n    }\n\n    public static int postage(int zone, int grams) {\n        return switch (zone) {\n            case 1 -> 150;\n            case 2 -> 300;\n            case 3 -> {\n                int over = Math.max(0, grams - 500);\n                yield 800 + over / 100 * 50;\n            }\n            default -> -1;\n        };\n    }\n}\n","hints":["Zone 3's case is a block: `case 3 -> { ... yield price; }`. Inside it, work out how many grams the parcel is over 500. A parcel under 500 grams is over by 0, not by a negative number: `Math.max(0, grams - 500)`.","Integer division drops the rest: `750 / 100` is 7, the number of full hundreds. Then `yield 800 + over / 100 * 50;`."],"tests":[{"name":"System.out.println(postage(3, 1250));","stdin":"","call":"System.out.println(postage(3, 1250));\n","expect":"1150","hidden":false},{"name":"System.out.println(postage(1, 2000) + \" \" + postage(2, 300));","stdin":"","call":"System.out.println(postage(1, 2000) + \" \" + postage(2, 300));\n","expect":"150 300","hidden":true},{"name":"System.out.println(postage(3, 400) + \" \" + postage(3, 500) + \" \" + postage(3, 599));","stdin":"","call":"System.out.println(postage(3, 400) + \" \" + postage(3, 500) + \" \" + postage(3, 599));\n","expect":"800 800 800","hidden":true},{"name":"System.out.println(postage(3, 600) + \" \" + postage(3, 3000));","stdin":"","call":"System.out.println(postage(3, 600) + \" \" + postage(3, 3000));\n","expect":"850 2050","hidden":true},{"name":"System.out.println(postage(4, 100) + \" \" + postage(0, 100));","stdin":"","call":"System.out.println(postage(4, 100) + \" \" + postage(0, 100));\n","expect":"-1 -1","hidden":true}],"require":[{"pattern":"\\bswitch\\s*\\(\\s*zone\\s*\\)","message":"Choose the price with `switch (zone)`."},{"pattern":"\\byield\\b","message":"Work out zone 3's price in a block that ends with `yield`."}],"forbid":[]}]}