Java switch null value

Java switch null value

A switch statement transfers control to one of several statements or expressions, depending on the value of its selector expression. In earlier releases, the selector expression must evaluate to a number, string or enum constant, and case labels must be constants. However, in this release, the selector expression can be of any type, and case labels can have patterns. Consequently, a switch statement or expression can test whether its selector expression matches a pattern, which offers more flexibility and expressiveness compared to testing whether its selector expression is exactly equal to a constant.

This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features.

For background information about pattern matching for switch expressions and statements, see JEP 406.

Consider the following code that calculates the perimeter of certain shapes from the section Pattern Matching for instanceof:

interface Shape < >record Rectangle(double length, double width) implements Shape < >record Circle(double radius) implements Shape < >. public static double getPerimeter(Shape shape) throws IllegalArgumentException < if (shape instanceof Rectangle r) < return 2 * r.length() + 2 * r.width(); >else if (shape instanceof Circle c) < return 2 * c.radius() * Math.PI; >else < throw new IllegalArgumentException("Unrecognized shape"); >>

You can rewrite this code to use a pattern switch expression as follows:

 public static double getPerimeter(Shape shape) throws IllegalArgumentException < return switch (shape) < case Rectangle r ->2 * r.length() + 2 * r.width(); case Circle c -> 2 * c.radius() * Math.PI; default -> throw new IllegalArgumentException("Unrecognized shape"); >; >

The following example uses a switch statement instead of a switch expression:

 public static double getPerimeter(Shape shape) throws IllegalArgumentException < switch (shape) < case Rectangle r: return 2 * r.length() + 2 * r.width(); case Circle c: return 2 * c.radius() * Math.PI; default: throw new IllegalArgumentException("Unrecognized shape"); >>

The type of a selector expression can either be an integral primitive type or any reference type (such as in the previous examples). The following switch expression matches the selector expression obj with type patterns that involve a class type, an enum type, a record type, and an array type:

record Point(int x, int y) < >enum Color < RED, GREEN, BLUE; >. static void typeTester(Object obj) < switch (obj) < case null ->System.out.println("null"); case String s -> System.out.println("String"); case Color c -> System.out.println("Color with " + c.values().length + " values"); case Point p -> System.out.println("Record class: " + p.toString()); case int[] ia -> System.out.println("Array of int values of length" + ia.length); default -> System.out.println("Something else"); > >

It’s possible that many pattern labels could match the value of the selector expression. To help readability, the labels are tested in the order that they appear in the switch block. In addition, the compiler raises an error when a pattern label can never match because a preceding one will always match first. The following example results in a compile-time error:

 static void error(Object obj) < switch(obj) < case CharSequence cs ->System.out.println("A sequence of length " + cs.length()); case String s -> // Error - pattern is dominated by previous pattern System.out.println("A string: " + s); default -> throw new IllegalStateException("Invalid argument"); > >

The first pattern label case CharSequence cs dominates the second pattern label case String s because every value that matches the pattern String s also matches the pattern CharSequence cs but not the other way around. It’s because String is a subtype of CharSequence .

You’ll get a compile-time error if any pattern dominates a subsequent pattern in a switch block.

There are two labels that match all values: the default label and a total type pattern (see Null-Matching case Labels). You can’t have more than one of these two labels in a switch block.

Type Coverage in switch Expressions

As described in Switch Expressions, cases of switch expressions must be exhaustive, which means that for all possible values, there must be a matching switch label. The following switch expression is not exhaustive and generates a compile-time error. Its type coverage consists of only types or subtypes of String or Integer , which doesn’t cover all possible values for obj :

 static int coverage(Object obj) < return switch (obj) < // Error - incomplete case String s ->s.length(); case Integer i -> i; >; >

However, the type coverage of the case label default is all types, so the following example compiles:

 static int coverage(Object obj) < return switch (obj) < // Error - incomplete case String s ->s.length(); case Integer i -> i; default -> 0; >; >

The compiler takes into account whether the type of a selector expression is a sealed class. The following switch expression compiles. It doesn’t need a default case label because its type coverage is the classes A , B , and C , which are the only permitted subclasses of S , the type of the selector expression:

sealed interface S permits A, B, C < >final class A implements S < >final class B implements S < >record C(int i) implements S < >// Implicitly final . static int testSealedCoverage(S s) < return switch (s) < case A a ->1; case B b -> 2; case C c -> 3; >; >

Scope of Pattern Variable Declarations

As described in the section Pattern Matching for instanceof, the scope of a pattern variable is the places where the program can reach only if the instanceof operator is true :

 public static double getPerimeter(Shape shape) throws IllegalArgumentException < if (shape instanceof Rectangle s) < // You can use the pattern variable s of type Rectangle here. >else if (shape instanceof Circle s) < // You can use the pattern variable s of type Circle here // but not the pattern variable s of type Rectangle. >else < // You cannot use either pattern variable here. >>

In a switch expression, you can use a pattern variable inside the expression, block, or throw statement that appears right of the arrow. For example:

 static void test(Object obj) < switch (obj) < case Character c -> < if (c.charValue() == 7) < System.out.println("Ding!"); >System.out.println("Character, value " + c.charValue()); > case Integer i -> System.out.println("Integer: " + i); default -> throw new IllegalStateException("Invalid argument"); > >

The scope of pattern variable c is the block to the right of case Character c -> . The scope of pattern variable i is the println statement to the right of case Integer I -> .

In a switch statement, you can use a case label’s pattern variable in its switch -labeled statement group. However, you can’t use it in any other switch -labeled statement group, even if the program flow can fall through a default statement group. For example:

 static void test(Object obj) < switch (obj) < case Character c: if (c.charValue() == 7) < System.out.print("Ding "); >if (c.charValue() == 9) < System.out.print("Tab "); >System.out.println("character, value " + c.charValue()); default: // You cannot use the pattern variable c here: throw new IllegalStateException("Invalid argument"); > >

The scope of pattern variable c consists of the case Character c statement group: the two if statements and the println statement that follows them. The scope doesn’t include the default statement group even though the switch statement can execute the case Character c statement group, fall through the default case label, and then execute the default statement group.

You will get a compile-time error if it’s possible to fall through a case label that declares a pattern variable. The following example doesn’t compile:

 static void test(Object obj) < switch (obj) < case Character c: if (c.charValue() == 7) < System.out.print("Ding "); >if (c.charValue() == 9) < System.out.print("Tab "); >System.out.println("character"); case Integer i: // Compile-time error System.out.println("An integer " + i); default: System.out.println("Neither character nor integer"); > >

If this code were allowed, and the value of the selector expression, obj , was a Character , then the switch statement can execute the case Character c statement group, then fall through the case Integer i case label, where the pattern variable i would have not been initialized.

Similarly, you can’t declare multiple pattern variables in a case label. The following aren’t permitted; either c or i would have been initialized (depending on the value of obj ).

 case Character c, Integer i: . case Character c, Integer i -> . 

Prior to this preview feature, switch expressions and switch statements throw a NullPointerException if the value of the selector expression is null. However, pattern labels offer more flexibility – there are now two new null-matching case labels. First, a null case label is available:

 static void test(Object obj) < switch (obj) < case null ->System.out.println("null!"); case String s -> System.out.println("String"); default -> System.out.println("Something else"); > >

This example prints null! when obj is null instead of throwing a NullPointerException .

Second, a pattern label whose pattern is a total type pattern matches null if the value of the selector expression is null. A type pattern, T t , is total for a type S if the type erasure of S is a subtype of the type erasure of T . For example, the type pattern Object obj is total for the type String . Consider the following switch statement:

 String s = . switch (s) < case Object obj ->. // total type pattern, so it matches null! >

The pattern label case Object obj applies if s evaluates to null.

If a selector expression evaluates to null and the switch block does not have a pattern label that is null-matching, then a NullPointerException is thrown as normal.

Источник

The switch Statement

Unlike if-then and if-then-else statements, the switch statement can have a number of possible execution paths. A switch works with the byte , short , char , and int primitive data types. It also works with enumerated types (discussed in Enum Types), the String class, and a few special classes that wrap certain primitive types: Character , Byte , Short , and Integer (discussed in Numbers and Strings).

The following code example, SwitchDemo , declares an int named month whose value represents a month. The code displays the name of the month, based on the value of month , using the switch statement.

public class SwitchDemo < public static void main(String[] args) < int month = 8; String monthString; switch (month) < case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; case 4: monthString = "April"; break; case 5: monthString = "May"; break; case 6: monthString = "June"; break; case 7: monthString = "July"; break; case 8: monthString = "August"; break; case 9: monthString = "September"; break; case 10: monthString = "October"; break; case 11: monthString = "November"; break; case 12: monthString = "December"; break; default: monthString = "Invalid month"; break; >System.out.println(monthString); > >

In this case, August is printed to standard output.

The body of a switch statement is known as a switch block. A statement in the switch block can be labeled with one or more case or default labels. The switch statement evaluates its expression, then executes all statements that follow the matching case label.

You could also display the name of the month with if-then-else statements:

int month = 8; if (month == 1) < System.out.println("January"); >else if (month == 2) < System.out.println("February"); >. // and so on

Deciding whether to use if-then-else statements or a switch statement is based on readability and the expression that the statement is testing. An if-then-else statement can test expressions based on ranges of values or conditions, whereas a switch statement tests expressions based only on a single integer, enumerated value, or String object.

Another point of interest is the break statement. Each break statement terminates the enclosing switch statement. Control flow continues with the first statement following the switch block. The break statements are necessary because without them, statements in switch blocks fall through: All statements after the matching case label are executed in sequence, regardless of the expression of subsequent case labels, until a break statement is encountered. The program SwitchDemoFallThrough shows statements in a switch block that fall through. The program displays the month corresponding to the integer month and the months that follow in the year:

public class SwitchDemoFallThrough < public static void main(String[] args) < java.util.ArrayListfutureMonths = new java.util.ArrayList(); int month = 8; switch (month) < case 1: futureMonths.add("January"); case 2: futureMonths.add("February"); case 3: futureMonths.add("March"); case 4: futureMonths.add("April"); case 5: futureMonths.add("May"); case 6: futureMonths.add("June"); case 7: futureMonths.add("July"); case 8: futureMonths.add("August"); case 9: futureMonths.add("September"); case 10: futureMonths.add("October"); case 11: futureMonths.add("November"); case 12: futureMonths.add("December"); break; default: break; >if (futureMonths.isEmpty()) < System.out.println("Invalid month number"); >else < for (String monthName : futureMonths) < System.out.println(monthName); >> > >

This is the output from the code:

August September October November December

Technically, the final break is not required because flow falls out of the switch statement. Using a break is recommended so that modifying the code is easier and less error prone. The default section handles all values that are not explicitly handled by one of the case sections.

The following code example, SwitchDemo2 , shows how a statement can have multiple case labels. The code example calculates the number of days in a particular month:

class SwitchDemo2 < public static void main(String[] args) < int month = 2; int year = 2000; int numDays = 0; switch (month) < case 1: case 3: case 5: case 7: case 8: case 10: case 12: numDays = 31; break; case 4: case 6: case 9: case 11: numDays = 30; break; case 2: if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) numDays = 29; else numDays = 28; break; default: System.out.println("Invalid month."); break; >System.out.println("Number of Days = " + numDays); > >

This is the output from the code:

Using Strings in switch Statements

In Java SE 7 and later, you can use a String object in the switch statement’s expression. The following code example, StringSwitchDemo , displays the number of the month based on the value of the String named month :

public class StringSwitchDemo < public static int getMonthNumber(String month) < int monthNumber = 0; if (month == null) < return monthNumber; >switch (month.toLowerCase()) < case "january": monthNumber = 1; break; case "february": monthNumber = 2; break; case "march": monthNumber = 3; break; case "april": monthNumber = 4; break; case "may": monthNumber = 5; break; case "june": monthNumber = 6; break; case "july": monthNumber = 7; break; case "august": monthNumber = 8; break; case "september": monthNumber = 9; break; case "october": monthNumber = 10; break; case "november": monthNumber = 11; break; case "december": monthNumber = 12; break; default: monthNumber = 0; break; >return monthNumber; > public static void main(String[] args) < String month = "August"; int returnedMonthNumber = StringSwitchDemo.getMonthNumber(month); if (returnedMonthNumber == 0) < System.out.println("Invalid month"); >else < System.out.println(returnedMonthNumber); >> >

The output from this code is 8 .

The String in the switch expression is compared with the expressions associated with each case label as if the String.equals method were being used. In order for the StringSwitchDemo example to accept any month regardless of case, month is converted to lowercase (with the toLowerCase method), and all the strings associated with the case labels are in lowercase.

Note: This example checks if the expression in the switch statement is null . Ensure that the expression in any switch statement is not null to prevent a NullPointerException from being thrown.

Источник

Читайте также:  Экономический бот дискорд python
Оцените статью