Скачать книгу

reasons, only the first and last case statements in the following example compile:

      final int getCookies() { return 4; } void feedAnimals() { final int bananas = 1; int apples = 2; int numberOfAnimals = 3; final int cookies = getCookies(); switch(numberOfAnimals) { case bananas: case apples: // DOES NOT COMPILE case getCookies(): // DOES NOT COMPILE case cookies : // DOES NOT COMPILE case 3 * 5 : } }

      The bananas variable is marked final, and its value is known at compile-time, so it is valid. The apples variable is not marked final, even though its value is known, so it is not permitted. The next two case statements, with values getCookies() and cookies, do not compile because methods are not evaluated until runtime, so they cannot be used as the value of a case statement, even if one of the values is stored in a final variable. The last case statement, with value 3 * 5, does compile, as expressions are allowed as case values, provided the value can be resolved at compile-time. They also must be able to fit in the switch data type without an explicit cast. We go into that in more detail shortly.

      Next, the data type for case statements must match the data type of the switch variable. For example, you can't have a case statement of type String if the switch statement variable is of type int, since the types are incomparable.

      The switch Expression

      Our second implementation of printDayOfWeek() was improved but still quite long. Notice that there was a lot of boilerplate code, along with numerous break statements. Can we do better? Yes, thanks to the new switch expressions that were officially added to Java 14.

      Because a switch expression is a compact form, there's a lot going on in Figure 3.4! For starters, we can now assign the result of a switch expression to a variable result. For this to work, all case and default branches must return a data type that is compatible with the assignment. The switch expression supports two types of branches: an expression and a block. Each has different syntactical rules on how it must be created. More on these topics shortly.

Schematic illustration of the structure of a switch expression

      Note Icon Recall from Chapter 2, “Operators,” that -> is the arrow operator. While the arrow operator is commonly used in lambda expressions, when it is used in a switch expression, the case branches are not lambdas.

      We can rewrite our previous printDayOfWeek() method in a much more concise manner using case expressions:

      Compare this code with the switch statement we wrote earlier. Both accomplish the same task, but a lot of the boilerplate code has been removed, leaving the behavior we care most about.

      Notice that a semicolon is required after each switch expression. For example, the following code does not compile. How many semicolons is it missing?

      var result = switch(bear) { case 30 -> "Grizzly" default -> "Panda" }

      The answer is three. Each case or default expression requires a semicolon as well as the assignment itself. The following fixes the code:

      var result = switch(bear) { case 30 -> "Grizzly"; default -> "Panda"; };

      As shown in Figure 3.4, case statements can take multiple values, separated by commas. Let's rewrite our printSeason() method from earlier using a switch expression:

      public void printSeason(int month) { switch(month) { case 1, 2, 3 -> System.out.print("Winter"); case 4, 5, 6 -> System.out.print("Spring"); case 7, 8, 9 -> System.out.print("Summer"); case 10, 11, 12 -> System.out.print("Fall"); } }

      Calling printSeason(2) prints the single value Winter. This time we don't have to worry about break statements, since only one branch is executed.

      Note Icon Most of the time, a switch expression returns a value, although printSeason() demonstrates one in which the return type is void. Since the type is void, it can't be assigned to a variable. On the exam, you are more likely to see a switch expression that returns a value, but you should be aware that it is possible.

      All of the previous rules around switch data types and case values still apply, although we have some new rules. Don't worry if these rules are new to you or you've never seen the yield keyword before; we'll be discussing them in the following sections.

      1 All of the branches of a switch expression that do not throw an exception must return a consistent data type (if the switch expression returns a value).

      2 If the switch expression returns a value, then every branch that isn't an expression must yield a value.

      3 A default branch is required unless all cases are covered or no value is returned.

      We cover the last rule shortly, but notice that our printSeason() example does not contain a default branch. Since the switch expression does not return a value and assign it to a variable, it is entirely optional.

      Note Icon Java 17 also supports pattern matching within switch expressions, but since this is a Preview feature, it is not in scope for the exam.

      Returning Consistent Data Types

      The first rule of using a switch expression is probably the easiest. You can't return incompatible or random data types. For example, can you see why three of the lines of the following code do not compile?

      int measurement = 10; int size = switch(measurement) { case 5 -> 1; case 10 -> (short)2; default -> 5; case 20 -> "3"; // DOES NOT COMPILE case 40 -> 4L; // DOES NOT COMPILE case 50 -> null; // DOES NOT COMPILE };

      Notice that the second case expression

Скачать книгу