'switch' Expressions
Java has significantly enhanced the switch
construct, turning it into a more powerful and expressive feature. A switch expression is one of the most important additions which enables switch
to produce a value.
Key Enhancements to switch
The modern Java switch
supports:
The switch expression
The
yield
statementArrow syntax (
->
) forcase
labelsLists of
case
constants
A Traditional use of switch case
// obtain the shipping method associated with a product ID.
class TraditionalSwitch
{
enum ShipMethod { STANDARD, TRUCK, AIR, OVERNIGHT }
public static void main(String[] args)
{
ShipMethod shipBy;
int productID = 5099;
// case stacking is used.
switch(productID)
{
case 1774:
case 8708:
case 6709:
shipBy = ShipMethod.TRUCK;
break;
case 4657:
case 2195:
case 3621:
case 1887:
shipBy = ShipMethod.AIR;
break;
case 2907:
case 5099:
shipBy = ShipMethod.OVERNIGHT;
break;
default:
shipBy = ShipMethod.STANDARD;
}
System.out.println("Shipping method for product number "
+ productID + " is " + shipBy);
}
}
Shipping method for product number 5099 is OVERNIGHT
A switch expression returns a value, making it usable in places like:
Assignments
Method arguments
Return values
int result = switch (value) {
case 1 -> 100;
case 2 -> 200;
default -> 0;
};
This is in contrast to traditional switch
statements, which do not produce values directly.
The yield
Statement
yield
is used to specify the result of a case
in a switch expression when more than a single expression is involved:
int result = switch (value) {
case 1 -> {
System.out.println("Processing case 1");
yield 100;
}
case 2 -> {
System.out.println("Processing case 2");
yield 200;
}
default -> 0;
};
NOTE
yield
immediately exits the switch expression with the given value, much like break
, but with the ability to return data.
yield is a context-sensitive keyword. This means that outside its use in a switch expression, yield is simply an identifier with no special meaning.
Arrow Syntax in case
Arrow syntax (->
) replaces the colon in case
labels. It brings several benefits:
No fall-through to next case, terminates at end of an arrow case (no need for
break
)Cleaner syntax for returning values for switch expressions.
Supports expressions, blocks, or throws (cannot be a statement )
// Simple expression
case 'A' -> 1;
// Block with yield
case 'B' -> {
System.out.println("Block executed");
yield 2;
}
// Throwing an exception
case 'C' -> throw new IllegalArgumentException("Invalid input");
List of 'case' Constants
You can now list multiple constants in a single case
using commas. This eliminates the need for "case stacking" and improves code readability.
case 1774, 8708, 6709:
shipBy = shipMethod.TRUCK;
break;
case 1774, 8708, 6709 -> ShipMethod.TRUCK;
class SwitchExprDemo
{
enum ShipMethod {STANDARD, TRUCK, AIR, OVERNIGHT }
public static void main(String[] args)
{
int productID = 5099;
ShipMethod shipBy = switch(productID)
{
// no break or yirld is needed
case 1774, 8708, 6709 -> ShipMethod.TRUCK;
case 4657, 2195, 1887, 3621 -> ShipMethod.AIR;
case 2907, 5099 -> ShipMethod.OVERNIGHT;
default -> ShipMethod.STANDARD;
}; // semicolon needed
System.out.println("Shipping method for product number " +
productID + " is " + shipBy);
}
}
Switch Expression Must Return a Value
Each path in a switch expression (including default
) must produce a result unless it throws an exception.
Incorrect:
int result = switch (value) {
case 1 -> 100;
case 2 -> {}; // ❌ Error: no value returned
default -> 0;
};
Correct:
int result = switch (value) {
case 1 -> 100;
case 2 -> {
yield 200;
}
default -> 0;
};
IMPORTANT
When switch is used in an assignment, it must be terminated by a semicolon.
class SwitchExprDemo
{
enum ShipMethod {STANDARD, TRUCK, AIR, OVERNIGHT }
public static void main(String[] args)
{
int productID = 5099;
ShipMethod shipBy = switch(productID)
{
case 1774, 8708, 6709:
yield ShipMethod.TRUCK;
case 4657, 2195, 1887, 3621:
yield ShipMethod.AIR;
case 2907, 5099:
yield ShipMethod.OVERNIGHT;
default:
yield ShipMethod.STANDARD;
}; // semicolon needed
System.out.println("Shipping method for product number " +
productID + " is " + shipBy);
}
}
NOTE
switch expression ensures each case yields a value. This yield causes immediate termination of the switch, so no fall through from case to case will occur.
There is an important restriction that applies to a switch expression: the case statements must handle all of the values that might occur. For this reason, most switch expressions will have a default statement.
The exception to this rule is when an enumeration is used, and each value of the enumeration is matched by a case.
A Closer Look at the Arrow case
The target of the -> can also be a block of code when more than one expression is needed.
IMPORTANT
Since the target of the arrow case is a block, yield
must be used to return a value.
class SwitchExprDemo
{
enum ShipMethod {STANDARD, TRUCK, AIR, OVERNIGHT }
public static void main(String[] args)
{
int productID = 5099;
boolean extraCharge;
ShipMethod shipBy = switch(productID)
{
case 1774, 8708, 6709 ->
{
extraCharge = true;
yield ShipMethod.TRUCK;
}
case 4657, 2195, 1887, 3621 ->
{
extraCharge = false;
yield ShipMethod.AIR;
}
case 2907, 5099 ->
{
extraCharge = true;
yield ShipMethod.OVERNIGHT;
}
default ->
{
extraCharge = false;
yield ShipMethod.STANDARD;
}
}; // semicolon needed
System.out.println("Shipping method for product number " +
productID + " is " + shipBy);
}
}
Even though block targets are used, each path through the switch expression must still provide a value.
Using ->
in traditional switch statement where no value is produced but no fall through can occur also.
class StatementSwitchWithArrows
{
public static void main(String[] args)
{
// Production line counters.
int line1count = 0;
int line2count = 0;
int line3count = 0;
int productionLine;
for(int i=1; i<10; i++)
{
productionLine = (i%3) +1;
switch(productionLine)
{
case 1 ->
{
line1count++;
System.out.println("Line 1 produced a unit.");
}
case 2 ->
{
line2count++;
System.out.println("Line 2 produced a unit.");
}
case 3 ->
{
line1count++;
System.out.println("Line 3 produced a unit.");
}
}
}
System.out.println("Total counts for Lines 1, 2, and 3: " +
line1count + ", " + line2count + ", "
+ line3count);
}
}
If this switch were an expression, then default would be needed because a switch expression is required to be exhaustive.
Because each case increases the value of a different variable, it would not be possible to transform this switch into an expression.
NOTE
you cannot mix arrow cases with traditional, colon cases in the same switch. You must choose one or the other.
Getting time zone using switch
class CityTZDemo
{
// Use an enumeration to describe the time zones.
enum TZ { Eastern, Central, Mountain, Pacific, Other }
public static void main(String[] args)
{
// An array of various cities in North America.
String[] cities =
{
"New York", "Boston", "Miami", "Chicago",
"St. Louis", "Des Moines", "Denver",
"Albuquerque", "Seattle", "San Francisco",
"Los Angeles", "Portland"
};
// Display the time zone for each city in the array.
for(String city: cities)
{
TZ zone = switch(city)
{
case "New York", "Boston", "Miami" -> TZ.Eastern;
case "Chicago", "St. Louis", "Des Moines" -> TZ.Central;
case "Albuquerque", "Denver" -> TZ.Mountain;
case "Seattle", "San Francisco", "Los Angeles", "Portland" -> TZ.Pacific;
default -> TZ.Other;
};
if(zone == TZ.Other)
System.out.println(city + " is outside the Continental US");
else
System.out.println(city + " is in the " + zone + " time zone");
}
}
}
New York is in the Eastern time zone
Boston is in the Eastern time zone
Miami is in the Eastern time zone
Chicago is in the Central time zone
St. Louis is in the Central time zone
Des Moines is in the Central time zone
Denver is in the Mountain time zone
Albuquerque is in the Mountain time zone
Seattle is in the Pacific time zone
San Francisco is in the Pacific time zone
Los Angeles is in the Pacific time zone
Portland is in the Pacific time zone