Hello, Java developers! I'm Igor Fraga, and this is the second post from a series called “The upgraded Java Developer” about the latest Java programming language enhancements to be up to date and use these features in your daily work.
Today we're diving into two powerful features that have revolutionized how we handle conditional logic in Java: Switch Expressions and Pattern Matching for Switch. Both features have evolved significantly since their introduction and are now fully supported in Java 21.
Wow, you will have it all in a single post, isn't that great?
Sure it is, you will find everything you need to know here, so let's get started!
What are Switch Expressions?
Switch Expressions allow the switch construct to be used as either a statement or an expression. They introduce a new, more compact syntax and eliminate common pitfalls associated with the traditional switch statement.
Before Switch Expressions
Before Java 12, achieving similar functionality required more verbose code:
// Traditional switch statement
String day = "MONDAY";
String typeOfDay;
switch (day) {
case "MONDAY":
case "TUESDAY":
case "WEDNESDAY":
case "THURSDAY":
case "FRIDAY":
typeOfDay = "Weekday";
break;
case "SATURDAY":
case "SUNDAY":
typeOfDay = "Weekend";
break;
default:
typeOfDay = "Invalid day";
break;
}
How Switch Expressions Work
Here's an example of how to use Switch Expressions:
String day = "MONDAY";
String typeOfDay = switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> "Invalid day";
};
In this example, we use the new ->
syntax to directly yield a value for each case. The switch construct is now an expression that returns a value.
As you can see, Switch Expressions offer a more concise and readable syntax.
Using the Yield Keyword
For more complex cases where you need to execute multiple statements, you can use the yield
keyword:
String result = switch (day) {
case "MONDAY" -> {
System.out.println("It's Monday!");
yield "Weekday";
}
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> {
System.out.println("Invalid day provided.");
yield "Invalid day";
}
};
In this example, the yield
keyword is used to return a value from a block of code within a case.
Why are Switch Expressions Better?
Compared to traditional switch statements, Switch Expressions offer several advantages:
Conciseness: They allow multiple cases to be handled in a single line.
Safety: They enforce exhaustive handling of all cases, reducing bugs.
Expressiveness: They can be used in contexts where an expression is expected.
No Fall-through: Each case is self-contained, eliminating accidental fall-through errors.
Evolution from Java 12 to Java 21
Switch Expressions were first introduced as a preview feature in Java 12. In Java 14, they became a standard feature. Java 21 fully supports them and introduces additional pattern matching capabilities (which we are going to cover in the next lines).
Best Practices
Use Switch Expressions when you need to assign the result of a switch to a variable.
Prefer the
->
syntax for simple, single-line cases.Use the
yield
keyword for more complex cases that require multiple statements.
Next, we're exploring Pattern Matching for Switch, a powerful feature that has evolved since its preview in Java 17 and is now a standard feature in Java 21. If you're used to Java 8, this enhancement will significantly change how you handle complex conditional logic in your code.
Revolutionizing Conditional Logic with Pattern Matching for Switch
Pattern Matching for Switch extends the capabilities of the switch statement and expression by allowing patterns in case labels. This feature enables more expressive and concise code when dealing with complex data structures and type-based comparisons.
Pattern Matching for Switch allows you to check not just simple values, but also types of objects and their contents. It's like a smarter, more powerful switch statement.
Here's an example of how to use Pattern Matching for Switch:
static String describeShape(Shape shape) {
return switch (shape) {
case Circle c -> "Circle with radius " + c.radius;
case Rectangle r -> "Rectangle " + r.width + " x " + r.height;
case Triangle t -> "Triangle with base " + t.base + " and height " + t.height;
case null -> "No shape";
default -> "Unknown shape";
};
}
In this example, we use type patterns to match against different subclasses of Shape, extracting their properties directly in the case label.
Pattern Matching for Switch also supports the when
clause for additional conditional refinements within a case. Here's an example:
static String describeRectangle(Rectangle rectangle) {
return switch (rectangle) {
case Rectangle r when r.width == r.height -> "Square with side " + r.width;
case Rectangle r -> "Rectangle " + r.width + " x " + r.height;
case null -> "No rectangle";
default -> "Unknown shape";
};
}
In this example, we use the when
clause to check if the rectangle is a square (i.e., its width equals its height). This allows us to provide more specific handling for certain conditions.
Before Java 17, achieving similar functionality required more verbose code:
// Traditional if-else chain
static String describeShape(Shape shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape;
return "Circle with radius " + c.radius;
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
return "Rectangle " + r.width + " x " + r.height;
} else if (shape instanceof Triangle) {
Triangle t = (Triangle) shape;
return "Triangle with base " + t.base + " and height " + t.height;
} else if (shape == null) {
return "No shape";
} else {
return "Unknown shape";
}
}
As you can see, Pattern Matching for Switch offers a more concise and readable syntax.
Why is Pattern Matching for Switch Better?
Pattern Matching for Switch offers several advantages:
Conciseness: It reduces boilerplate code, eliminating the need for explicit type checks and casts, so no need to add tons of
instanceof
and casts anymore to validate the types.Type Safety: The compiler ensures exhaustive matching, reducing the risk of runtime errors.
Readability: Complex conditional logic becomes more straightforward and easier to understand.
Flexibility: It allows matching against types, constants, and even null values in a unified syntax.
Evolution from Java 17 to Java 21
Pattern Matching for Switch has evolved through several preview versions:
Introduced as a preview in Java 17 (JEP 406)
Refined in Java 18 (JEP 420) and Java 19 (JEP 427)
Further enhanced in Java 20 (JEP 433)
Finally standardized in Java 21 (JEP 441)
Each iteration brought improvements based on community feedback, resulting in a more robust and versatile feature.
Best Practices
Use Pattern Matching for Switch when dealing with complex type hierarchies or data structures.
Leverage the null case for explicit null handling.
Combine with record patterns for even more powerful data extraction.
Use the
when
clause for additional conditional refinements within a case.
Conclusion
Switch Expressions and Pattern Matching for Switch in Java 21 represent a significant leap forward in Java's ability to handle complex conditional logic. They make our code more expressive, safer, and easier to maintain, especially when working with polymorphic types or intricate data structures.
In our next article from this series, we'll explore Text Blocks, another feature that has greatly improved string handling in Java. Stay tuned to learn how this feature can make working with multi-line strings a breeze!
Top comments (0)