Switch expressions with arrow syntax (->) were introduced as a permanent feature in Java 14, providing a more concise, readable, and less error-prone alternative to traditional switch statements.
1. Basic Switch Expressions
Traditional vs New Switch Syntax
public class BasicSwitchExpressions {
public static void main(String[] args) {
demonstrateBasicSyntax();
demonstrateReturningValues();
demonstrateMultipleCases();
}
public static void demonstrateBasicSyntax() {
System.out.println("=== Basic Switch Expression Syntax ===");
int day = 3;
// ❌ Traditional switch statement (verbose)
String dayTypeOld;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
dayTypeOld = "Weekday";
break;
case 6:
case 7:
dayTypeOld = "Weekend";
break;
default:
dayTypeOld = "Invalid day";
}
System.out.println("Traditional: " + dayTypeOld);
// ✅ New switch expression with arrow syntax (concise)
String dayTypeNew = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid day";
};
System.out.println("New syntax: " + dayTypeNew);
// Without storing in variable
System.out.println("Direct usage: " +
switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid day";
}
);
}
public static void demonstrateReturningValues() {
System.out.println("\n=== Returning Values from Switch ===");
int month = 2;
int year = 2024;
// Switch expression returning a value
int daysInMonth = switch (month) {
case 1, 3, 5, 7, 8, 10, 12 -> 31;
case 4, 6, 9, 11 -> 30;
case 2 -> isLeapYear(year) ? 29 : 28;
default -> throw new IllegalArgumentException("Invalid month: " + month);
};
System.out.printf("Month %d, Year %d has %d days%n", month, year, daysInMonth);
// Using yield for complex logic (Java 13+)
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
case 6, 7, 8 -> "Summer";
case 9, 10, 11 -> "Autumn";
default -> {
System.err.println("Invalid month: " + month);
yield "Unknown";
}
};
System.out.println("Season: " + season);
}
private static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
public static void demonstrateMultipleCases() {
System.out.println("\n=== Multiple Case Labels ===");
char grade = 'B';
String message = switch (grade) {
case 'A', 'B' -> "Excellent";
case 'C' -> "Good";
case 'D' -> "Pass";
case 'F' -> "Fail";
default -> "Invalid grade";
};
System.out.println("Grade " + grade + ": " + message);
// With different data types
Object value = "Hello";
String type = switch (value) {
case String s -> "String: " + s.length() + " characters";
case Integer i -> "Integer: " + i;
case Double d -> "Double: " + d;
case null -> "Null value";
default -> "Other type: " + value.getClass().getSimpleName();
};
System.out.println("Value type: " + type);
}
}
2. Advanced Switch Expression Features
Yield Keyword and Code Blocks
public class AdvancedSwitchExpressions {
public static void main(String[] args) {
demonstrateYieldKeyword();
demonstrateCodeBlocks();
demonstrateComplexLogic();
}
public static void demonstrateYieldKeyword() {
System.out.println("=== Yield Keyword ===");
int score = 85;
// Using yield for complex expressions
String grade = switch (score) {
case 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 -> "A";
case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 -> "B";
case 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 -> "C";
case 60, 61, 62, 63, 64, 65, 66, 67, 68, 69 -> "D";
default -> {
if (score < 0) {
yield "Invalid: Score cannot be negative";
} else if (score < 60) {
yield "F";
} else {
yield "Invalid: Score too high";
}
}
};
System.out.println("Score " + score + ": Grade " + grade);
// yield with calculations
int quantity = 25;
double price = 10.0;
double totalCost = switch (quantity) {
case 1 -> price;
case 2, 3, 4 -> price * quantity * 0.9; // 10% discount
case 5, 6, 7, 8, 9 -> price * quantity * 0.8; // 20% discount
default -> {
double baseCost = price * quantity;
if (quantity >= 50) {
yield baseCost * 0.6; // 40% discount for bulk
} else {
yield baseCost * 0.7; // 30% discount
}
}
};
System.out.printf("Quantity %d: Total cost $%.2f%n", quantity, totalCost);
}
public static void demonstrateCodeBlocks() {
System.out.println("\n=== Code Blocks in Switch ===");
String input = "HELLO";
String processed = switch (input.toLowerCase()) {
case "hello" -> {
System.out.println("Processing greeting...");
yield "Greeting processed: " + input.toUpperCase();
}
case "goodbye" -> {
System.out.println("Processing farewell...");
yield "Farewell processed: " + input.toLowerCase();
}
case "" -> {
System.out.println("Empty input detected");
yield "Empty input";
}
case String s when s.length() > 10 -> {
System.out.println("Processing long input...");
String truncated = s.substring(0, 10) + "...";
yield "Long input truncated: " + truncated;
}
default -> {
System.out.println("Processing default case...");
yield "Default processing: " + input;
}
};
System.out.println("Result: " + processed);
}
public static void demonstrateComplexLogic() {
System.out.println("\n=== Complex Logic in Switch ===");
// User role and permissions example
String userRole = "MODERATOR";
String action = "DELETE_POST";
boolean hasPermission = switch (userRole) {
case "ADMIN" -> true; // Admins can do everything
case "MODERATOR" -> switch (action) {
case "CREATE_POST", "EDIT_POST", "DELETE_POST" -> true;
case "BAN_USER" -> false;
default -> false;
};
case "USER" -> switch (action) {
case "CREATE_POST", "EDIT_OWN_POST" -> true;
default -> false;
};
case "GUEST" -> action.equals("VIEW_POSTS");
default -> false;
};
System.out.printf("User '%s' %s permission for '%s'%n",
userRole, hasPermission ? "has" : "does not have", action);
// Nested switch expressions
processOrder("ELECTRONICS", "HIGH", 2);
processOrder("BOOKS", "LOW", 5);
}
public static void processOrder(String category, String priority, int quantity) {
double basePrice = 100.0;
double finalPrice = switch (category) {
case "ELECTRONICS" -> switch (priority) {
case "HIGH" -> basePrice * quantity * 1.1; // 10% premium
case "NORMAL" -> basePrice * quantity;
case "LOW" -> basePrice * quantity * 0.9; // 10% discount
default -> basePrice * quantity;
};
case "BOOKS" -> switch (priority) {
case "HIGH" -> basePrice * quantity * 1.05; // 5% premium
case "NORMAL", "LOW" -> basePrice * quantity * 0.8; // 20% discount
default -> basePrice * quantity;
};
case "CLOTHING" -> basePrice * quantity * 0.7; // 30% discount
default -> basePrice * quantity;
};
System.out.printf("Category: %s, Priority: %s, Quantity: %d -> Final Price: $%.2f%n",
category, priority, quantity, finalPrice);
}
}
3. Real-World Use Cases
Business Logic Applications
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.*;
public class RealWorldSwitchExpressions {
public static void main(String[] args) {
demonstrateBusinessLogic();
demonstrateConfiguration();
demonstrateValidation();
demonstrateStateMachines();
}
public static void demonstrateBusinessLogic() {
System.out.println("=== Business Logic Examples ===");
// Shipping cost calculation
String shippingMethod = "EXPRESS";
double weight = 2.5;
String destination = "INTERNATIONAL";
double shippingCost = calculateShippingCost(shippingMethod, weight, destination);
System.out.printf("Shipping cost: $%.2f%n", shippingCost);
// Tax calculation based on location and product type
String state = "CA";
String productType = "ELECTRONICS";
double amount = 1000.0;
double tax = calculateTax(state, productType, amount);
System.out.printf("Tax for %s in %s: $%.2f%n", productType, state, tax);
}
public static double calculateShippingCost(String method, double weight, String destination) {
return switch (method) {
case "STANDARD" -> switch (destination) {
case "LOCAL" -> 5.0 + (weight * 0.5);
case "DOMESTIC" -> 10.0 + (weight * 1.0);
case "INTERNATIONAL" -> 25.0 + (weight * 2.0);
default -> throw new IllegalArgumentException("Unknown destination: " + destination);
};
case "EXPRESS" -> switch (destination) {
case "LOCAL" -> 15.0 + (weight * 1.0);
case "DOMESTIC" -> 25.0 + (weight * 2.0);
case "INTERNATIONAL" -> 50.0 + (weight * 5.0);
default -> throw new IllegalArgumentException("Unknown destination: " + destination);
};
case "SAME_DAY" -> 75.0 + (weight * 10.0);
default -> throw new IllegalArgumentException("Unknown shipping method: " + method);
};
}
public static double calculateTax(String state, String productType, double amount) {
return switch (state) {
case "CA" -> switch (productType) {
case "FOOD", "MEDICINE" -> amount * 0.01; // 1% for essentials
case "ELECTRONICS" -> amount * 0.085; // 8.5% for electronics
case "CLOTHING" -> amount * 0.06; // 6% for clothing
default -> amount * 0.075; // 7.5% standard
};
case "NY" -> switch (productType) {
case "FOOD" -> amount * 0.0; // No tax on food in NY
case "ELECTRONICS" -> amount * 0.08875; // 8.875% for electronics
default -> amount * 0.04; // 4% standard
};
case "TX" -> amount * 0.0625; // Flat 6.25% in Texas
case "OR", "AK", "DE", "MT", "NH" -> 0.0; // No sales tax states
default -> amount * 0.05; // 5% default for other states
};
}
public static void demonstrateConfiguration() {
System.out.println("\n=== Configuration Examples ===");
// Log level configuration
String logLevel = "DEBUG";
Set<String> enabledLogs = switch (logLevel) {
case "TRACE" -> Set.of("TRACE", "DEBUG", "INFO", "WARN", "ERROR");
case "DEBUG" -> Set.of("DEBUG", "INFO", "WARN", "ERROR");
case "INFO" -> Set.of("INFO", "WARN", "ERROR");
case "WARN" -> Set.of("WARN", "ERROR");
case "ERROR" -> Set.of("ERROR");
case "OFF" -> Set.of();
default -> throw new IllegalArgumentException("Unknown log level: " + logLevel);
};
System.out.println("Enabled logs: " + enabledLogs);
// Feature flags based on environment
String environment = "PRODUCTION";
Map<String, Boolean> features = switch (environment) {
case "DEVELOPMENT" -> Map.of(
"newUI", true,
"experimental", true,
"debugMode", true,
"analytics", false
);
case "STAGING" -> Map.of(
"newUI", true,
"experimental", true,
"debugMode", false,
"analytics", true
);
case "PRODUCTION" -> Map.of(
"newUI", false,
"experimental", false,
"debugMode", false,
"analytics", true
);
default -> Map.of();
};
System.out.println("Feature flags: " + features);
}
public static void demonstrateValidation() {
System.out.println("\n=== Validation Examples ===");
// Email validation
String email = "[email protected]";
ValidationResult emailResult = switch (email) {
case String s when s == null || s.isBlank() ->
ValidationResult.invalid("Email cannot be empty");
case String s when !s.contains("@") ->
ValidationResult.invalid("Email must contain @ symbol");
case String s when !s.matches("^[A-Za-z0-9+_.-]+@(.+)$") ->
ValidationResult.invalid("Invalid email format");
case String s when s.length() > 254 ->
ValidationResult.invalid("Email too long");
default -> ValidationResult.valid();
};
System.out.println("Email validation: " + emailResult);
// Password strength validation
String password = "SecurePass123!";
PasswordStrength strength = switch (password) {
case String p when p == null || p.length() < 8 ->
PasswordStrength.TOO_WEAK;
case String p when p.length() >= 8 && !p.matches(".*[A-Z].*") ->
PasswordStrength.WEAK;
case String p when p.length() >= 8 && p.matches(".*[A-Z].*") &&
!p.matches(".*[0-9].*") ->
PasswordStrength.MEDIUM;
case String p when p.length() >= 12 && p.matches(".*[A-Z].*") &&
p.matches(".*[0-9].*") && p.matches(".*[!@#$%^&*()].*") ->
PasswordStrength.STRONG;
case String p when p.length() >= 8 && p.matches(".*[A-Z].*") &&
p.matches(".*[0-9].*") ->
PasswordStrength.GOOD;
default -> PasswordStrength.WEAK;
};
System.out.println("Password strength: " + strength);
}
public static void demonstrateStateMachines() {
System.out.println("\n=== State Machine Examples ===");
// Order state machine
OrderState currentState = OrderState.PLACED;
OrderEvent event = OrderEvent.PAYMENT_RECEIVED;
OrderState nextState = processOrderEvent(currentState, event);
System.out.printf("Order moved from %s to %s on %s%n",
currentState, nextState, event);
// Workflow approval process
ApprovalState approvalState = ApprovalState.SUBMITTED;
String approverRole = "MANAGER";
boolean canApprove = canApprove(approvalState, approverRole);
System.out.printf("Can %s approve %s? %s%n",
approverRole, approvalState, canApprove);
}
public static OrderState processOrderEvent(OrderState current, OrderEvent event) {
return switch (current) {
case PLACED -> switch (event) {
case PAYMENT_RECEIVED -> OrderState.PAID;
case CANCELLED -> OrderState.CANCELLED;
default -> throw new IllegalStateException("Invalid event for PLACED: " + event);
};
case PAID -> switch (event) {
case SHIPPED -> OrderState.SHIPPED;
case REFUND_REQUESTED -> OrderState.REFUND_PENDING;
default -> throw new IllegalStateException("Invalid event for PAID: " + event);
};
case SHIPPED -> switch (event) {
case DELIVERED -> OrderState.DELIVERED;
case LOST -> OrderState.LOST;
default -> throw new IllegalStateException("Invalid event for SHIPPED: " + event);
};
case DELIVERED -> current; // Terminal state
case CANCELLED -> current; // Terminal state
case REFUND_PENDING -> switch (event) {
case REFUND_APPROVED -> OrderState.REFUNDED;
case REFUND_REJECTED -> OrderState.PAID;
default -> throw new IllegalStateException("Invalid event for REFUND_PENDING: " + event);
};
case REFUNDED -> current; // Terminal state
case LOST -> current; // Terminal state
};
}
public static boolean canApprove(ApprovalState state, String role) {
return switch (state) {
case SUBMITTED -> switch (role) {
case "MANAGER", "DIRECTOR" -> true;
default -> false;
};
case MANAGER_APPROVED -> switch (role) {
case "DIRECTOR", "VP" -> true;
default -> false;
};
case DIRECTOR_APPROVED -> role.equals("VP");
case VP_APPROVED -> false; // Final state
case REJECTED -> false; // Terminal state
};
}
}
// Supporting classes for real-world examples
class ValidationResult {
private final boolean valid;
private final String message;
private ValidationResult(boolean valid, String message) {
this.valid = valid;
this.message = message;
}
public static ValidationResult valid() {
return new ValidationResult(true, "Valid");
}
public static ValidationResult invalid(String message) {
return new ValidationResult(false, message);
}
@Override
public String toString() {
return valid ? "VALID" : "INVALID: " + message;
}
}
enum PasswordStrength {
TOO_WEAK, WEAK, MEDIUM, GOOD, STRONG
}
enum OrderState {
PLACED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUND_PENDING, REFUNDED, LOST
}
enum OrderEvent {
PAYMENT_RECEIVED, CANCELLED, SHIPPED, DELIVERED, LOST,
REFUND_REQUESTED, REFUND_APPROVED, REFUND_REJECTED
}
enum ApprovalState {
SUBMITTED, MANAGER_APPROVED, DIRECTOR_APPROVED, VP_APPROVED, REJECTED
}
4. Pattern Matching in Switch (Java 21+)
Type Patterns and Guarded Patterns
public class PatternMatchingSwitch {
public static void main(String[] args) {
demonstrateTypePatterns();
demonstrateGuardedPatterns();
demonstrateNullHandling();
demonstrateRecordPatterns();
}
public static void demonstrateTypePatterns() {
System.out.println("=== Type Patterns in Switch ===");
Object[] objects = {
"Hello World",
42,
3.14,
List.of(1, 2, 3),
new int[]{1, 2, 3},
null,
'A',
LocalDate.now()
};
for (Object obj : objects) {
String description = describeObject(obj);
System.out.println(description);
}
}
public static String describeObject(Object obj) {
return switch (obj) {
case String s -> "String: '" + s + "' (length: " + s.length() + ")";
case Integer i -> "Integer: " + i + " (squared: " + (i * i) + ")";
case Double d -> "Double: " + d + " (formatted: " + String.format("%.2f", d) + ")";
case List<?> list -> "List with " + list.size() + " elements: " + list;
case int[] array -> "int array with " + array.length + " elements";
case LocalDate date -> "Date: " + date + " (day of week: " + date.getDayOfWeek() + ")";
case Character c -> "Character: '" + c + "' (Unicode: " + (int) c + ")";
case null -> "Null object";
default -> "Unknown type: " + obj.getClass().getSimpleName();
};
}
public static void demonstrateGuardedPatterns() {
System.out.println("\n=== Guarded Patterns (when clause) ===");
Object[] values = {
"Hello",
"",
" ",
"Very long string that exceeds normal limits",
null,
100,
-5,
0
};
for (Object value : values) {
String result = processWithGuard(value);
System.out.println(value + " -> " + result);
}
}
public static String processWithGuard(Object value) {
return switch (value) {
case String s when s.isBlank() -> "Empty or blank string";
case String s when s.length() > 20 -> "Very long string: " + s.substring(0, 20) + "...";
case String s when s.length() > 10 -> "Long string: " + s;
case String s -> "Normal string: " + s;
case Integer i when i > 0 -> "Positive integer: " + i;
case Integer i when i < 0 -> "Negative integer: " + i;
case Integer i -> "Zero";
case null -> "Null value";
default -> "Other: " + value;
};
}
public static void demonstrateNullHandling() {
System.out.println("\n=== Null Handling in Switch ===");
Object[] possiblyNull = {
"Hello",
null,
"World",
null,
"!"
};
for (Object obj : possiblyNull) {
String result = handleNull(obj);
System.out.println(obj + " -> " + result);
}
}
public static String handleNull(Object obj) {
return switch (obj) {
case null -> "Explicitly handling null case";
case String s when s.length() == 1 -> "Single character: '" + s + "'";
case String s -> "String: " + s;
default -> "Other: " + obj;
};
}
public static void demonstrateRecordPatterns() {
System.out.println("\n=== Record Patterns in Switch (Java 21+) ===");
Object[] shapes = {
new Point(10, 20),
new Circle(new Point(5, 5), 10.0),
new Rectangle(new Point(0, 0), new Point(10, 10)),
"Not a shape"
};
for (Object shape : shapes) {
String description = describeShape(shape);
System.out.println(description);
}
}
public static String describeShape(Object shape) {
return switch (shape) {
case Point(int x, int y) ->
"Point at coordinates (" + x + ", " + y + ")";
case Circle(Point center, double radius) ->
"Circle with center " + center + " and radius " + radius;
case Rectangle(Point topLeft, Point bottomRight) ->
"Rectangle from " + topLeft + " to " + bottomRight;
case null -> "Null shape";
default -> "Not a recognized shape: " + shape.getClass().getSimpleName();
};
}
// Complex pattern matching with nested records
public static void processComplexObject(Object obj) {
switch (obj) {
case Employee(String name, Department(String deptName, Manager mgr))
when deptName.equals("Engineering") -> {
System.out.println(name + " works in Engineering under " + mgr.name());
}
case Employee(String name, Department dept) -> {
System.out.println(name + " works in " + dept.name());
}
default -> System.out.println("Unknown object type");
}
}
}
// Record definitions for pattern matching
record Point(int x, int y) {
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
}
record Circle(Point center, double radius) {}
record Rectangle(Point topLeft, Point bottomRight) {}
record Employee(String name, Department department) {}
record Department(String name, Manager manager) {}
record Manager(String name) {}
5. Best Practices and Pitfalls
Switch Expression Guidelines
import java.util.*;
public class SwitchBestPractices {
public static void main(String[] args) {
demonstrateBestPractices();
demonstrateCommonPitfalls();
demonstrateRefactoringExamples();
}
public static void demonstrateBestPractices() {
System.out.println("=== Best Practices ===");
// ✅ GOOD: Exhaustive switching
DayOfWeek day = DayOfWeek.MONDAY;
String dayType = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
// No default needed - all enum values covered
};
System.out.println(day + " is a " + dayType);
// ✅ GOOD: Using default for error handling
int value = 999;
String result = switch (value) {
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
default -> throw new IllegalArgumentException("Unexpected value: " + value);
};
// ✅ GOOD: Complex logic in blocks
String input = "complex input";
String output = switch (input) {
case "simple" -> "processed simply";
case "complex" -> {
System.out.println("Starting complex processing...");
// Multiple lines of complex logic
String temp = input.toUpperCase();
yield "Complex result: " + temp;
}
default -> "default processing";
};
System.out.println(output);
}
public static void demonstrateCommonPitfalls() {
System.out.println("\n=== Common Pitfalls ===");
// ❌ BAD: Forgetting that switch expressions must cover all cases
/*
int x = 5;
String badExample = switch (x) {
case 1 -> "One";
case 2 -> "Two";
// Missing default - compilation error!
};
*/
// ✅ GOOD: Always provide complete coverage
int x = 5;
String goodExample = switch (x) {
case 1 -> "One";
case 2 -> "Two";
default -> "Other";
};
// ❌ BAD: Using switch expressions for side effects only
/*
switch (x) {
case 1 -> System.out.println("One"); // Just side effects
case 2 -> System.out.println("Two");
default -> System.out.println("Other");
}
*/
// ✅ GOOD: Use traditional statements for side effects only
switch (x) {
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Other");
}
// ❌ BAD: Overly complex switch expressions
String overlyComplex = switch (x) {
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
case 5 -> "Five";
case 6 -> "Six";
case 7 -> "Seven";
case 8 -> "Eight";
case 9 -> "Nine";
case 10 -> "Ten";
default -> {
if (x > 1000) {
yield "Very large number";
} else if (x < 0) {
yield "Negative number";
} else {
yield "Medium number";
}
}
};
// ✅ GOOD: Extract complex logic
String better = getNumberDescription(x);
System.out.println(better);
}
private static String getNumberDescription(int x) {
if (x >= 1 && x <= 10) {
return switch (x) {
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
case 5 -> "Five";
case 6 -> "Six";
case 7 -> "Seven";
case 8 -> "Eight";
case 9 -> "Nine";
case 10 -> "Ten";
default -> throw new IllegalStateException("Unexpected value: " + x);
};
} else if (x > 1000) {
return "Very large number";
} else if (x < 0) {
return "Negative number";
} else {
return "Medium number";
}
}
public static void demonstrateRefactoringExamples() {
System.out.println("\n=== Refactoring Examples ===");
// Before refactoring - nested if-else
String type = "A";
int value = 10;
String oldResult;
if ("A".equals(type)) {
if (value > 0) {
oldResult = "Positive A";
} else if (value < 0) {
oldResult = "Negative A";
} else {
oldResult = "Zero A";
}
} else if ("B".equals(type)) {
if (value > 0) {
oldResult = "Positive B";
} else {
oldResult = "Non-positive B";
}
} else {
oldResult = "Unknown type";
}
System.out.println("Old approach: " + oldResult);
// After refactoring - switch expression
String newResult = switch (type) {
case "A" -> switch (value) {
case int v when v > 0 -> "Positive A";
case int v when v < 0 -> "Negative A";
default -> "Zero A";
};
case "B" -> value > 0 ? "Positive B" : "Non-positive B";
default -> "Unknown type";
};
System.out.println("New approach: " + newResult);
}
public static void showStyleGuidelines() {
System.out.println("\n=== Style Guidelines ===");
System.out.println("""
✅ DO:
- Use switch expressions when you need to return a value
- Prefer arrow syntax over traditional colon syntax
- Use multiple case labels for related values
- Use yield for complex logic in code blocks
- Handle all possible cases (be exhaustive)
- Use pattern matching with instanceof checks
- Keep switch expressions focused and readable
❌ DON'T:
- Use switch expressions only for side effects
- Create overly complex nested switch expressions
- Mix arrow and traditional syntax unnecessarily
- Forget to handle the default case
- Use switch when simple if-else would be clearer
- Create deeply nested patterns that are hard to read
📐 FORMATTING:
- Align arrows vertically for readability
- Use blocks with yield for multi-line logic
- Group related cases together
- Place default case at the end
""");
}
}
6. Performance Considerations
Switch Expression Performance
public class SwitchPerformance {
public static void main(String[] args) {
demonstratePerformanceComparison();
showOptimizationTips();
}
public static void demonstratePerformanceComparison() {
System.out.println("=== Switch Expression Performance ===");
int[] testValues = generateTestValues(100000);
// Warm up JIT compiler
for (int i = 0; i < 10000; i++) {
processWithIfElse(testValues[i % testValues.length]);
processWithSwitchStatement(testValues[i % testValues.length]);
processWithSwitchExpression(testValues[i % testValues.length]);
}
// Benchmark if-else
long ifElseStart = System.nanoTime();
for (int value : testValues) {
processWithIfElse(value);
}
long ifElseTime = System.nanoTime() - ifElseStart;
// Benchmark traditional switch
long switchStart = System.nanoTime();
for (int value : testValues) {
processWithSwitchStatement(value);
}
long switchTime = System.nanoTime() - switchStart;
// Benchmark switch expression
long expressionStart = System.nanoTime();
for (int value : testValues) {
processWithSwitchExpression(value);
}
long expressionTime = System.nanoTime() - expressionStart;
System.out.printf("If-else: %,d ns%n", ifElseTime);
System.out.printf("Traditional switch: %,d ns%n", switchTime);
System.out.printf("Switch expression: %,d ns%n", expressionTime);
System.out.printf("Switch expression vs if-else: %.2fx%n",
(double) ifElseTime / expressionTime);
System.out.printf("Switch expression vs traditional: %.2fx%n",
(double) switchTime / expressionTime);
}
public static String processWithIfElse(int value) {
if (value == 1) {
return "One";
} else if (value == 2) {
return "Two";
} else if (value == 3) {
return "Three";
} else if (value == 4) {
return "Four";
} else if (value == 5) {
return "Five";
} else {
return "Other";
}
}
public static String processWithSwitchStatement(int value) {
switch (value) {
case 1: return "One";
case 2: return "Two";
case 3: return "Three";
case 4: return "Four";
case 5: return "Five";
default: return "Other";
}
}
public static String processWithSwitchExpression(int value) {
return switch (value) {
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
case 5 -> "Five";
default -> "Other";
};
}
private static int[] generateTestValues(int size) {
int[] values = new int[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
values[i] = random.nextInt(10); // 0-9
}
return values;
}
public static void showOptimizationTips() {
System.out.println("\n=== Performance Optimization Tips ===");
System.out.println("""
1. Switch expressions have similar performance to traditional switches
2. JVM can optimize both approaches effectively
3. For dense integer cases, switches use tableswitch (O(1))
4. For sparse cases, switches use lookupswitch (O(log n))
5. Pattern matching may have slight overhead but improves readability
6. Focus on code clarity first, optimize only if profiling shows issues
7. Complex patterns with guards may impact performance
""");
// Demonstrate different switch optimizations
demonstrateSwitchOptimizations();
}
public static void demonstrateSwitchOptimizations() {
System.out.println("=== Switch Optimization Examples ===");
// Dense cases - will use tableswitch
int denseValue = 3;
String denseResult = switch (denseValue) {
case 0 -> "Zero";
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
default -> "Other";
};
System.out.println("Dense switch result: " + denseResult);
// Sparse cases - will use lookupswitch
int sparseValue = 1000;
String sparseResult = switch (sparseValue) {
case 1 -> "One";
case 10 -> "Ten";
case 100 -> "Hundred";
case 1000 -> "Thousand";
case 10000 -> "Ten thousand";
default -> "Other";
};
System.out.println("Sparse switch result: " + sparseResult);
}
}
Conclusion
Key Benefits of Switch Expressions:
- Conciseness: Less boilerplate code
- Readability: Clearer intent and structure
- Safety: Exhaustiveness checking prevents bugs
- Expressiveness: Can return values directly
- Modern Syntax: Arrow syntax is more intuitive
When to Use Switch Expressions:
| Scenario | Recommendation |
|---|---|
| Returning values based on conditions | ✅ Perfect use case |
| Multiple related conditions | ✅ Great for grouping cases |
| Complex logic with blocks | ✅ Use yield in code blocks |
| Pattern matching | ✅ Excellent with type patterns |
| Side effects only | ❌ Use traditional statements |
Java Version Support:
- Java 12-13: Preview feature (enable with
--enable-preview) - Java 14+: Standard feature
- Java 17+: Enhanced with pattern matching
- Java 21+: Full pattern matching support
Switch expressions with arrow syntax represent a significant improvement in Java's expressiveness and safety, making conditional logic more concise and less error-prone while maintaining excellent performance characteristics.