{"id":409395,"date":"2024-01-30T07:04:43","date_gmt":"2024-01-30T06:04:43","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=idea&#038;p=409395"},"modified":"2024-01-30T15:34:13","modified_gmt":"2024-01-30T14:34:13","slug":"evolution-of-the-switch-construct-in-java-why-should-you-care","status":"publish","type":"idea","link":"https:\/\/blog.jetbrains.com\/idea\/2024\/01\/evolution-of-the-switch-construct-in-java-why-should-you-care\/","title":{"rendered":"Evolution of the Switch Construct in Java\u2014Why Should you Care?"},"content":{"rendered":"\n<p>Even though defining conditions in code is one of the programming basics, developers didn\u2019t use the <em>old<\/em> <code>switch<\/code> statements often because this feature was quite constrained in its capabilities. This is not the case anymore. With the addition of switch expressions and Pattern Matching, the switch construct has evolved a lot\u2014it can return values, switch over multiple types, compare objects and not just constants in case labels, and much more. It can replace long <code>if-else<\/code> constructs and older switch statements with concise code, for use cases that you might not know about. It has fewer bugs, is easier to read, write, and understand, and is a charm to work with.\u00a0<\/p>\n\n\n\n<p>Don\u2019t worry if all these new features overwhelm you. IntelliJ IDEA can help you get started with using the modern switch construct. It can detect if-else code\/ old switch style that could be replaced with the new switch, even if you don\u2019t know anything about its syntax or usage. IntelliJ IDEA can also prompt you to use the switch in the correct or improved way.<\/p>\n\n\n\n<p>In this blog post, I\u2019ll cover all these changes using hands-on coding examples to highlight their benefits. I\u2019ll also address a few of the frequently asked questions by developers, such as, why does it seem that the switch has been changing over a long time, why, and how.\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Understanding the Bigger Picture<\/h2>\n\n\n\n<p>Before we deep dive into the sample code, let us answer a few frequently asked questions by developers on this topic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 What changed with the switch construct, how and when?<\/h3>\n\n\n\n<p>The humble switch construct has come a long way, and is now available in two flavors &#8211; Switch Expressions and Switch Statements. The first change was initiated via <a href=\"https:\/\/openjdk.org\/jeps\/361\" target=\"_blank\" rel=\"noopener\"><em>Switch Expressions<\/em><\/a><em>,<\/em> (Java 14), and the addition of <a href=\"https:\/\/openjdk.org\/jeps\/441\" target=\"_blank\" rel=\"noopener\"><em>Pattern Matching for Switch<\/em><\/a> (Java 21) has revolutionized the ways in which it can be used. In Java 23, you might be able to use <a href=\"https:\/\/openjdk.org\/jeps\/455\" target=\"_blank\" rel=\"noopener\">primitive types in patterns<\/a> too.&nbsp;<\/p>\n\n\n\n<p>Unlike a switch statement, Switch expressions can <em>return<\/em> a value, without needing a <code>break<\/code> statement at the end of each case label. Pattern matching for switch significantly reduces the count of lines of code you need for an equivalent if-else construct. It enables you to switch on a lot of data types rather than just a handful of primitives, enums and String (as was the case with the older switch constructs). With pattern matching for switch, you can handle null values as a case label, match different data types or <a href=\"https:\/\/openjdk.org\/jeps\/440\" target=\"_blank\" rel=\"noopener\">record patterns<\/a> and initialize pattern variables. You can also refine the conditions in case labels by using <code>when<\/code>, and much more. Targeted for Java 23, switch patterns would be able to include primitive data types in patterns.<\/p>\n\n\n\n<p>However, changes to the switch construct over multiple Java version releases have left some developers confused &#8211; regarding the different changes that were added to it or dropped, current state of affairs and the ways it can be used now.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 Why are developers confused about the changes made to switch?<\/h3>\n\n\n\n<p>With Java&#8217;s six-month release cadence, changes to its existing language features, like the switch construct, or addition of new features like the Pattern Matching, are introduced as&nbsp; <a href=\"https:\/\/openjdk.org\/jeps\/12\" target=\"_blank\" rel=\"noopener\"><em>preview feature<\/em><\/a> &#8211; features that are complete, but impermanent.&nbsp;<\/p>\n\n\n\n<p>Such features might take up to two or more Java versions to become permanent in the language. For example, Switch Expressions was introduced in Java 12, but was permanently added in Java in its version 14. Sometimes it is confusing to remember when a feature was introduced or if it is still in preview. Though developers are encouraged to try out or experiment with the preview features, using them in production code is not recommended.<\/p>\n\n\n\n<p>Also, preview features <em>might<\/em> change over two or more Java versions, until they are permanently added to the Java language. For example, when the Switch Expressions were introduced as a preview language feature in Java 12, its case label used the keyword <em>break<\/em> to return a value, which was later changed to the keyword <em>yield<\/em>. Though these changes are few, they might add up to the confusion &#8211; what was changed, and when? Talking about \u2018when\u2019, guarded patterns in Java 17 and 18 used <em>&amp;<\/em> to define a guarded pattern, which has been changed to the usage of <em>when<\/em> since Java version 19. A preview feature could be dropped altogether if sufficient feedback suggests that. <a href=\"https:\/\/openjdk.org\/jeps\/326\" target=\"_blank\" rel=\"noopener\">Raw String Literals<\/a> is one such example.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 Why were \u2018switch\u2019 constructs not used enough in the past?<\/h3>\n\n\n\n<p>Sequence, condition and iteration\u2014are the three basic programming structures to create instructions or algorithms in our code bases.<\/p>\n\n\n\n<p>When it comes to defining conditions; nothing beats the <code>if<\/code>\/ <code>if-else<\/code> constructs. You have used them to define the simplest or the most complex conditions, using any type of variables or constants. So, we developers usually preferred if\/ if-else constructs over other options like the (old) switch construct and ternary operators for defining conditions because the later two had very limited capabilities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.4 Why bother using \u2018switch\u2019 now?<\/h3>\n\n\n\n<p>With all the changes that I mentioned in the previous sections, switch is the new cool kid on the block. It can also be used in use cases where only if-else could be used earlier. Also, developers realized that if\/ if-else constructs have disadvantages too\u2014the long <code>if\/else<\/code> constructs are not easy to read. They can\u2019t force developers to define a set of conditions at the language level (you might need an <code>else<\/code> condition even when it is not needed). Also, optimizing code that uses if\/ if-else is difficult and has O(n) time complexity, as opposed to O(1) time complexity for switch.&nbsp;<\/p>\n\n\n\n<p>Let&#8217;s work with coding examples to understand benefits of using the new evolved switch in everyday coding.<\/p>\n\n\n<h2 class=\"wp-block-heading\">2. Returning values with Switch Expressions<\/h2>\n<p>As compared to the \u2018old\u2019 switch constructs, switch expressions can return a value, starting with Java 14. Why is this a big deal? Let\u2019s find out using a few commonly occurring use cases.<\/p>\n<h3 class=\"wp-block-heading\">2.1. Replace switch statement with concise switch expressions<\/h3>\n<p>Imagine a common use case of calculating a discount for an order via a formula (lambda) that depends on a customer\u2019s loyalty card type and their loyalty points. Before switch expressions, you might code this functionality using a switch statement (that can\u2019t return a value) as shown in the code below.<\/p>\n<p>It was common to switch over the card type owned by a customer, create a local variable, say, <code>result<\/code>, at the beginning of the method, conditionally assign the lambda to calculate the discount in each switch case, and return its value at the end of the method (assume that the variable <code>a<\/code> represents the order total and variable <code>b<\/code> represents the points accumulated in the customer\u2019s loyalty card):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\nenum CardType {SILVER, GOLD, PLATINUM, DIAMOND}\n\nprivate static BiFunction&lt;Double, Integer, Double&gt; getOrderDiscountFormula(CardType cardType) {\n    BiFunction&lt;Double, Integer, Double&gt; result;\n    switch (cardType) {\n        case SILVER:\n            result = (a, b) -&gt; (a * 0) + b;\n            break;\n        case GOLD:\n            result = (a, b) -&gt; (a * .05) + b;\n            break;\n        case PLATINUM:\n            result = (a, b) -&gt; (a * 0.1) + b * 2;\n            break;\n        case DIAMOND:\n            result = (a, b) -&gt; (a * 0.15) + b * 3;\n            break;\n        default:\n            throw new IllegalArgumentException(&quot;Unexpected value: &quot; + cardType);\n    }\n    return result;\n}<\/pre>\n<p>Whenever you see, hear or experience the word repetition (in the coding or development ecosystem), beware! That is the perfect place for bugs to hide.<\/p>\n<p>The preceding code has multiple issues:<\/p>\n<ul>\n<li><b>Code Repetition<\/b>: Each case label needs a <code>break<\/code> statement to exit \u2018switch\u2019 after code in a case label executes. Code <code>result =<\/code> is repeated in each case label.<\/li>\n<li><b>Potential logical errors<\/b>: If you miss adding the <code>break<\/code> statement in any of the case labels, it will result in a default fall-through. What that means is, the flow control falls to the next case label (until it reaches another <code>break<\/code> statement) and executes the corresponding code, assigning another value to the variable <code>result<\/code>, which would be incorrect.<\/li>\n<li><b>Writing unreachable code<\/b>: In this specific example, the default case label would never execute because the enum <code>CardType<\/code> defines four values. Still you must write it or the code won\u2019t compile (error message-variable \u2018result\u2019 might not have been initialized).<\/li>\n<\/ul>\n<p>Let\u2019s see how we could address all of these issues by replacing the switch statement with switch expressions, via the following gif :<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/switch-expressions.gif\" alt=\"\" \/><\/figure>\n<p>Here\u2019s the final code from the preceding gif (as text) for your reference:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">private static BiFunction&lt;Double, Integer, Double&gt; getOrderDiscountFormula(CardType cardType) {\n    return switch (cardType) {\n        case SILVER -&gt; (a, b) -&gt; (a * 0) + b;\n        case GOLD -&gt; (a, b) -&gt; (a * .05) + b;\n        case PLATINUM -&gt; (a, b) -&gt; (a * 0.1) + b * 2;\n        case DIAMOND -&gt; (a, b) -&gt; (a * 0.15) + b * 3;\n    };\n}<\/pre>\n<p>By replacing a switch construct with switch expressions, we could:<\/p>\n<ul>\n<li>Make this code concise and easier to understand.<\/li>\n<li>Use a switch expression to return a value from a method.<\/li>\n<li>Drop the duplicate code to assign value to result in each use case (result = ).<\/li>\n<li>Delete the default case labels, since all possible values for the enum CardType were already addressed.<\/li>\n<\/ul>\n<p>That\u2019s not all. With switch switch expressions, you can define multiple case labels for a switch branch, to execute the same code for multiple switch case labels.<\/p>\n<p>For an example, imagine a new <code>CardType<\/code>, say, <code>CORPORATE<\/code>, is added to the enum. Even though this card might offer different benefits, it is possible that it offers the same benefits on the order discounts as offered by an existing card type, say, the <code>PLATINUM<\/code> card. In this case, you would be able to add the label <code>CORPORATE<\/code> to the existing switch branch for <code>PLATINUM<\/code>, as follows (multiple values in a switch label were not allowed before the introduction of switch expressions):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">enum CardType { SILVER, GOLD, PLATINUM, DIAMOND, CORPORATE}\n\nprivate static BiFunction&lt;Double, Integer, Double&gt; getOrderDiscountFormula(CardType cardType) {\n    return switch (cardType) {\n        case SILVER -&gt; (a, b) -&gt; (a * 0) + b;\n        case GOLD -&gt; (a, b) -&gt; (a * .05) + b;\n        case PLATINUM, CORPORATE -&gt; (a, b) -&gt; (a * 0.1) + b * 2;\n        case DIAMOND -&gt; (a, b) -&gt; (a * 0.15) + b * 3;\n    };\n}<\/pre>\n<p>It is quite likely that you might see the code example we started with in this section coded using an if-else construct instead of an switch statement. Are there any benefits to converting an if-else statement to a switch expression. Let\u2019s find out in the next section.<\/p>\n<h3>2.2. If feasible, prefer Switch expressions over if\/else statements for improved readability<\/h3>\n<p>If\/else statements let you define complex conditions, which may not be feasible with switch constructs. But, if an if\/else construct could be replaced with a switch construct, go for it because it would make your code easier to understand.<\/p>\n<p>Let\u2019s rewrite the code sample from the previous section using an if-else statement instead of a switch statement, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n   private static BiFunction&lt;Double, Integer, Double&gt; getOrderDiscountFormula(CardType cardType) {\n        BiFunction&lt;Double, Integer, Double&gt; result;\n        if (cardType == CardType.SILVER) {\n            result = (a, b) -&gt; (a * 0) + b;\n        } else if (cardType == CardType.GOLD) {\n            result =  (a, b) -&gt; (a * .05) + b;\n        } else if (cardType == CardType.PLATINUM) {\n            result = (a, b) -&gt; (a * 0.1) + b * 2;\n        } else if (cardType == CardType.DIAMOND) {\n            result = (a, b) -&gt; (a * 0.15) + b * 3;\n        } else {\n            throw new IllegalArgumentException(&quot;Unexpected CardType value: &quot; + cardType);\n        }\n        return result; \n    }<\/pre>\n<p>You might ask-what are the issues with the preceding code?<\/p>\n<p>Since we humans are used to reading text from top to bottom, an if-else statement reads like a long winding answer to a question. If you call the preceding method with a value <code>CardType.DIAMOND<\/code>, it could seem like answering multiple questions, such as, is the value <code>CardType.SILVER<\/code>, if not, is it <code>GOLD<\/code>, <code>PLATINUM<\/code> or <code>DIAMOND<\/code>?<\/p>\n<p>This flow control is shown using the gif below that uses a debugger. Even though the inline variable value shows the value <code>true<\/code> next to the condition \u2018<code>cardType == CardType.DIAMOND<\/code>\u2019:, the control flow moves from top to bottom:<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/debugger-if.gif\" alt=\"\" \/><\/figure>\n<p>The next obvious question is &#8211; which is better in terms of performance. In theory, an if-else construct is difficult to optimize since it has O(n) time complexity; switch has O(1) time complexity. In practice, compiler optimizations may close this gap.<\/p>\n<p>You could manually convert the preceding code to use a switch expression. But, it is error-prone. I\u2019d recommend using IntelliJ IDEA\u2019s context action-Replace \u2018if\u2019 with \u2018switch\u2019, as follows:<\/p>\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/01\/convert-if-to-switch-2.png\" alt=\"\" \/><\/figure>\n<p>Quick note-if you have set the language level to 17 or later in IntelliJ IDEA, you\u2019d also notice the label <code>null<\/code> with the <code>default<\/code> label after the preceding conversion.<\/p>\n<p>When you use the debugger to see the flow of the control flow for a switch construct, you\u2019ll notice it requires much less keystrokes, as shown in the following gif:<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/01\/debugger-switch.gif\" alt=\"\" \/><\/figure>\n<h3>2.3. Refactor code and use switch expressions<\/h3>\n<p>Often using just a switch expression might not be enough to see how it could improve the readability of your code. Consider (modified) code of a method from an open source library that was probably written prior to the existence of switch expressions:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">    \n\tpublic boolean getValueAsBoolean(boolean defaultValue) throws IOException {\n        MyClass myclass = this.currentToken;\n        if (myclass != null) {\n            switch (myclass.id()) {\n                case 6:\n                    String str = this.getText().trim();\n                    if (&quot;true&quot;.equals(str)) {\n                        return true;\n                    }\n\n                    if (&quot;false&quot;.equals(str)) {\n                        return false;\n                    }\n\n                    if (&quot;null&quot;.equals(str)) {\n                        return false;\n                    }\n                    break;\n                case 7:\n                    return this.getIntValue() != 0;\n                case 8:\n                default:\n                    break;\n                case 9:\n                    return true;\n                case 10:\n                case 11:\n                    return false;\n                case 12:\n                    Object value = this.getEmbeddedObject();\n                    if (value instanceof Boolean) {\n                        return (Boolean)value;\n                    }\n            }\n        }\n        return defaultValue;\n    }<\/pre>\n<p>The preceding code could be improved by &#8211; extracting code for the label 6 to a separate method, refactoring the initial conditions and replacing the switch statement with a switch, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public boolean getValueAsBoolean2(boolean defaultValue) throws IOException {\n        if (currentToken == null || currentToken.id() == 8) {\n            return defaultValue;\n        }\n        return switch (currentToken.id()) {\n            case 6 -&gt; checkAndGetBooleanValue();\n            case 7 -&gt; getIntValue() != 0;\n            case 9 -&gt; true;\n            case 10, 11 -&gt; false;\n            case 12 -&gt; getEmbeddedObject() instanceof Boolean value &amp;&amp; (boolean) value;\n            default -&gt; defaultValue;\n        };\n    }\n\n    private boolean checkAndGetBooleanValue() throws IOException {\n        String trimmedText = getText().trim();\n        return switch (trimmedText) {\n            case &quot;true&quot;, &quot;null&quot; -&gt; true;\n            case &quot;false&quot; -&gt; false;\n            default -&gt; throw new IllegalArgumentException(&quot;Unexpected value: &quot; + trimmedText);\n        };\n    }<\/pre>\n<h3>2.4. Using Stream API with switch expressions as method arguments<\/h3>\n<p>Since switch expressions can return a value, you could pass it as arguments to methods too. It also means being able to use different coding approaches, such as being able to use the Stream API, instead of a for loop. For an example, the following code:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">private static StringBuilder handleSpecialCharacters(StringBuilder sb, String segment) {\n    int i = 0;\n    for (int end = segment.length(); i &lt; end; ++i) {\n        char c = segment.charAt(i);\n        switch (c) {\n            case &#039;+&#039;:\n                sb.append(&quot;~2&quot;);\n                break;\n            case &#039;-&#039;:\n                sb.append(&quot;~9&quot;);\n                break;\n            case &#039;*&#039;:\n                sb.append(&quot;~5&quot;);\n                break;\n            default:\n                sb.append(c);\n                break;\n        }\n    }\n    return sb;\n}<\/pre>\n<p>Could be coded using switch expressions and Stream API, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">private static StringBuilder handleSpecialCharacters(StringBuilder sb, String segment) {\n    segment.chars()\t\n           .mapToObj(c -&gt; (char) c)\n           .map(c -&gt; switch(c){\n               case &#039;+&#039; -&gt; &quot;~2&quot;;\n               case &#039;-&#039; -&gt; &quot;~9&quot;;\n               case &#039;*&#039; -&gt; &quot;~5&quot;;\n               default -&gt; String.valueOf(c);\n           })\n           .forEach(sb::append);\n    return sb;\n}<\/pre>\n<p>Using a single line lambda is usually preferred over multiline lambdas. For the preceding code, you could extract the switch expression to a method to achieve it, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">private static StringBuilder handleSpecialCharacters2(StringBuilder sb, String segment) {\n    segment.chars()\n           .mapToObj(c -&gt; (char) c)\n           .map(c -&gt; mapCharacter(c))\n           .forEach(sb::append);\n    return sb;\n}\n\nprivate static String mapCharacter(Character c) {\n    return switch (c) {\n        case &#039;\/&#039; -&gt; &quot;~1&quot;;\n        case &#039;~&#039; -&gt; &quot;~0&quot;;\n        case &#039;*&#039; -&gt; &quot;~5&quot;;\n        default -&gt; String.valueOf(c);\n    };\n}<\/pre>\n<p>In all the preceding example code segments, you noticed the usage of an arrow syntax for switch expressions for its case labels. Does it imply it could never use the colon syntax? Let\u2019s find out in the next section.<\/p>\n<h3>2.5. Switch statements vs. Switch expressions and Colon Syntax vs. Arrow Syntax<\/h3>\n<p>The switch statements co-exist with the switch expressions and they both can use the colon and arrow syntax with their case labels. Switch expressions could use the colon syntax and still return a value using the yield keyword. The following image shows examples of switch statements and switch expressions using both of these syntax to help you to compare them:<\/p>\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/01\/arrow-vs-colon-image2.png\" alt=\"\" \/><\/figure>\n<p>Introduction of switch expressions to Java in version 14 didn\u2019t expand the count of data types that a switch construct could switch on. This still remained a major hurdle that prevented it from being used widely by developers. Don\u2019t worry! The good news is that with pattern matching (production feature in Java 21), you can switch on many more data types. Let\u2019s check out this benefit with many more in the next section.<\/p>\n<h2>3. Pattern Matching for switch &#8211; comparing objects and more<\/h2>\n<p>Pattern matching for switch is a production feature in Java 21, which means you can use it in your code without any fears of it being changed in the future Java versions.<\/p>\n<p>Let\u2019s list how Pattern Matching for switch enhances the switch statements and expressions, helping you to write complex data queries with ease:<\/p>\n<ul>\n<li><b>What can you switch on<\/b>: You can switch on many more types than just the primitive data types, wrapper classes, enums and Strings.<\/li>\n<li><b>Type Test Pattern<\/b>: You can test the type of a variable in a switch label<\/li>\n<li><b>Pattern variable<\/b>: Testing of the variable type is followed by declaring a pattern variable.<\/li>\n<li><b>Refining Type Test pattern<\/b>: You can refine Type test patterns in switch labels by adding a condition using the keyword when.<\/li>\n<li><b>No explicit casting required<\/b>: You can call methods on the pattern variable without any explicit casting.<\/li>\n<li><b>What can switch labels include<\/b>: Switch labels are not limited to comparing constant values. You can check null, constants, and type of an instance in a switch case.<\/li>\n<li><b>Record Patterns in switch labels<\/b>: Switch labels allow you to use record patterns too.<\/li>\n<\/ul>\n<p>If you are new to Pattern Matching in switch or Pattern matching in general, I\u2019d recommend you to refer to <a href=\"https:\/\/blog.jetbrains.com\/idea\/2021\/09\/java-17-and-intellij-idea\/#what-is-pattern-matching\">this section<\/a> in my blog post on <a href=\"https:\/\/blog.jetbrains.com\/idea\/2021\/09\/java-17-and-intellij-idea\/\">Java 17 and IntelliJ IDEA<\/a>, which covers all the basics like what is pattern matching, how to use it with the <code>instanceof<\/code> operator and switch constructs, what are their benefits, and many more topics like how it reduces the cognitive load for you.<\/p>\n<p>In this blog post, I\u2019ll cover the benefits of using pattern matching in switch using hands-on examples.<\/p>\n<h3>3.1 Switch on user defined data types<\/h3>\n<p>Here\u2019s a common use case. Imagine you need to create a method that determines item wise discount for items in an invoice, using the following rules:<\/p>\n<p>40% discount &#8211; all books<\/p>\n<p>20% discount &#8211; all electronics<\/p>\n<p>30% discount &#8211; all apparel<\/p>\n<p>10% discount &#8211; rest of the items<\/p>\n<p>Assume you create the following interface and records to model these types:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">sealed interface SaleItem{}\nrecord Book(String title, double price) implements SaleItem { }\nrecord Electronics(String name, double price) implements SaleItem { }\nrecord Apparel(String type, String size, double price) implements SaleItem { }<\/pre>\n<p>The gif below shows how you can code a method, say, <code>computeDiscount<\/code>, which accepts a type of <code>SaleItem<\/code>, switches on it and, depending on its actual type, calculates the discount using the business rules defined above.<\/p>\n<p>The following gif also highlights IntelliJ IDEA\u2019s support for pattern matching with switch (with sealed types). You can use the <code>.switch<\/code> postfix operator on the variable <code>item<\/code>, which passes the variable <code>item<\/code> as a selector pattern to switch. Switch is used as an expression here (visible via a <code>return<\/code> keyword preceding its usage), and the interface <code>SaleItem<\/code> is defined as a sealed type. IntelliJ IDEA can determine these conditions and generate all the case labels for this switch expression by invoking Context Actions (Alt + Enter) and choosing \u2018Create missing branches\u2019. This support in the IDE is quite helpful if you are new to this Java feature or its syntax:<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/switch-pattern1.gif\" alt=\"\" \/><\/figure>\n<p>Here\u2019s the resultant code for your reference:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\npublic static double computeDiscount(SaleItem item) {\n    return switch (item) {\n        case Apparel apparel -&gt; 0.3 * apparel.price();\n        case Book book -&gt; 0.4 * book.price();\n        case Electronics electronics -&gt; 0.2 * electronics.price();\n    };\n}<\/pre>\n<p>The preceding code has multiple enhancements, which are specific to pattern matching for switch:<\/p>\n<ul>\n<li>The code is concise.<\/li>\n<li>The type of selector expression to switch is a user-defined type, that is, <code>SaleItem<\/code>. This was not possible earlier.<\/li>\n<li>Each switch branch uses a Type pattern (<code>Apparel<\/code>\/ <code>Book<\/code>\/ <code>Electronics<\/code>) and declares a pattern variable (<code>book<\/code>, <code>electronics<\/code> and <code>apparel<\/code>). When the type of selector expression matches with the type defined in a switch branch, that pattern variable is initialized (you don\u2019t need explicit casting).<\/li>\n<\/ul>\n<p>Since a switch construct can switch over many more types, it makes sense to check if the value of the selector pattern is null within the switch construct. With pattern matching for switch, you can define null as a case label, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\npublic static double computeDiscount(SaleItem item) {\n    return switch (item) {\n        case Apparel apparel -&gt; 0.3 * apparel.price();\n        case Book book -&gt; 0.4 * book.price();\n        case Electronics electronics -&gt; 0.2 * electronics.price();\n        case null -&gt; 0;\n    };\n}<\/pre>\n<p>Prior to addition of Pattern Matching, you might have coded the preceding method as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public static double computeDiscount(SaleItem item) {\n        if (item instanceof Apparel) {\n            Apparel apparel = (Apparel)item;\n            return 0.3 * apparel.price();\n        } else if (item instanceof Book) {\n            Book book = (Book)item;\n            return 0.4 * book.price();\n        } else if (item instanceof Electronics) {\n            Electronics electronics = (Electronics)item;\n            return 0.2 * electronics.price();\n        }\n        else {\n            return 0;\n        }\n    }<\/pre>\n<p>One of the main issues with the preceding code is repetition. All if-else blocks use the <code>instanceof<\/code> operator to check the type of the method parameter item, and then create another variable, explicitly casting it to the type it was checked against the instanceof operator in the preceding line of code. This is repetition overkill. The good news is that this if-else statement could be replaced with the modern switch construct.<\/p>\n<p>If you are using IntelliJ IDEA, you would notice that it can detect such code and notify you by highlighting the <code>if<\/code> keyword. On invoking context actions (Alt + Enter), it prompts you to-Replace \u2018if\u2019 with \u2018switch\u2019, as shown in the following gif:<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/01\/switch-if-to-patternmatching.gif\" alt=\"\" \/><\/figure>\n<p>One of other benefits of using pattern matching for switch is that it saves you from writing code for situations that would never occur. If you use if\/else construct for the above code, you would still need to code the else part, irrespective of the fact there are no subtypes of SaleItem, apart from Apparel, Book and Electronics. The default clause is not required for code version that uses pattern matching with switch.<\/p>\n<p>Imagine the business rules to calculate item wise discount change for apparels based on their sizes. Lets see how you can add conditions with a Type pattern in the next section.<\/p>\n<h3>3.2 Simplifying the code to calculate discounted price (using WHEN in switch labels)<\/h3>\n<p>The new business rules to calculate discount on apparels are:<\/p>\n<p>60% discount &#8211; all apparel of size XXS<\/p>\n<p>30% discount &#8211; all apparel of sizes other than XXS.<\/p>\n<p>One of the obvious ways to implement this business logic is to add a condition for on the right side of -&gt; for Type pattern Apparel, using if-else to check if the size equals XXS or not, and <code>yield<\/code> to return a value::<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public static double computeDiscount(SaleItem item) {\n        return switch (item) {\n            case Apparel apparel -&gt; {\n                if (apparel.size().equals(&quot;XXS&quot;)) {\n                    yield 0.6 * apparel.price();\n                } else {\n                    yield 0.3 * apparel.price();\n                }\n            }\n            case Book book -&gt; 0.4 * book.price();\n            case Electronics electronics -&gt; 0.2 * electronics.price();\n            case null, default -&gt; 0;\n        };\n    }<\/pre>\n<p>Let\u2019s see if we could simplify the preceding code by using a ternary operator instead of an if-else construct as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public static double computeDiscount(SaleItem item) {\n        return switch (item) {\n            case Apparel apparel -&gt; apparel.size().equals(&quot;XXS&quot;) ? \n                                        0.6 * apparel.price() : \n                                        0.3 * apparel.price();\n            case Book book -&gt; 0.4 * book.price();\n            case Electronics electronics -&gt; 0.2 * electronics.price();\n            case null, default -&gt; 0;\n        };\n    }<\/pre>\n<p>The last code looks better. But wait! With Pattern Matching for switch you can add a condition by using <code>when<\/code> in a case label. If you have worked with databases and queries, you\u2019ll love and prefer the following code that uses when (it is also better in terms of readability):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\npublic static double computeDiscount(SaleItem item) {\n    return switch (item) {\n        case Apparel apparel when apparel.size().equals(&quot;XXS&quot;)  -&gt; 0.6 * apparel.price();\n        case Apparel apparel                                    -&gt; 0.3 * apparel.price();\n        case Book book                                          -&gt; 0.4 * book.price();\n        case Electronics electronics                            -&gt; 0.2 * electronics.price();\n        case null, default                                      -&gt; 0;\n    };\n}<\/pre>\n<p>The keyword <code>when<\/code> is followed by an expression that returns a boolean value. This implies that you could add more complex conditions, or even call a method that returns a boolean value.<\/p>\n<p>Take a moment to register that the condition that includes when with the type <code>Apparel<\/code> in the preceding code is more specific than the switch branch that follows it. It is compilation error to swap the order of these case labels (as shown in the following gif):<\/p>\n<figure class=\"wp-block-image alignnone wp-image-16062\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/01\/patternmatching-label-domination.gif\" alt=\"\" \/><\/figure>\n<p>In the preceding code, the record Apparel defined multiple components, which are referred to in the switch branch. You also use Record pattern to deconstruct it to its components in pattern variables- to make it easy to refer to them.<\/p>\n<h3>3.3 Record patterns &#8211; reduce repetitions in code<\/h3>\n<p>In the preceding code, the switch label that calculates the discount for Apparel for size XXS needs to use the pattern variable apparel repeatedly to access its components size and price. You could use record patterns to deconstruct a record to its components, rewrite the code from the above section as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public static double computeDiscount(SaleItem item) {\n        return switch (item) {\n            case Apparel (String type, String size, double price) \n                    when size.equals(&quot;XXS&quot;)                         -&gt; 0.6 * price;\n            case Apparel apparel                                    -&gt; 0.3 * apparel.price();\n            case Book book                                          -&gt; 0.4 * book.price();\n            case Electronics electronics                            -&gt; 0.2 * electronics.price();\n            case null, default                                      -&gt; 0;\n        };\n    }<\/pre>\n<p>Record patterns could be used in multiple use case, for example to define recursive method calls, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\nsealed interface TwoDimensional {}\nrecord Point (int x, int y) implements TwoDimensional { }\nrecord Line    ( Point start, \n                 Point end) implements TwoDimensional { }\nrecord Triangle( Point pointA, \n                 Point pointB, \n                 Point PointC) implements TwoDimensional { }\nrecord Square  ( Point pointA, \n                 Point pointB, \n                 Point PointC, \n                 Point pointD) implements TwoDimensional { }\n\nstatic int process(TwoDimensional twoDim) {\n   return switch (twoDim) {\n       case Point(int x, int y) -&gt; x + y;\n       case Line(Point a, Point b) -&gt; process(a) + process(b);\n       case Triangle(Point a, Point b, Point c) -&gt; \n                                 process(a) + process(b) + process(c);\n       case Square(Point a, Point b, Point c, Point d) -&gt; \n                                 process(a) + process(b) + process(c) + process(d);\n   };\n}<\/pre>\n<p>If you are new to Record Patterns, please check out my <a href=\"https:\/\/blog.jetbrains.com\/idea\/2022\/11\/java-19-and-intellij-idea\/#record-patterns-and-generics\">detailed blog post on this topic<\/a> to find out more about how to use it with nested records, var type, generics and more.<\/p>\n<p>The preceding code (calculating item wise discounts) uses record patterns in the case labels, specifying all the components of a record. However, all of these component variables are not used. Let\u2019s check if it is mandatory to specify all the component variables in the next section.<\/p>\n<h3>3.4 Ignoring unused variables by using Unnamed Pattern in Record Patterns<\/h3>\n<p>Added as preview feature in Java 21,Unnamed variables and Patterns continues to be a preview feature in Java 22, It allows you to replace unused variables in record patterns and other places via an underscore, that is, <code>_<\/code>.<\/p>\n<p>Let\u2019s replace the unused component of the Record <code>Apparel<\/code>, that is, <code>String type<\/code>, with an <code>_<\/code>, in the record pattern, as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\n    public static double computeDiscount(SaleItem item) {\n        return switch (item) {\n            case Apparel (_, String size, double price) \n                    when size.equals(&quot;XXS&quot;)\t\t\t-&gt; 0.6 * price;\n            case Apparel apparel                            -&gt; 0.3 * apparel.price();\n            case Book book                                  -&gt; 0.4 * book.price();\n            case Electronics electronics                    -&gt; 0.2 * electronics.price();\n            case null, default                              -&gt; 0;\n        };\n    }<\/pre>\n<p>This feature comes in quite handy for records and nested that might use record patterns with multiple unused variables, such as in the following example:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"\">\nrecord Name       (String fName, String lName) { }\nrecord PhoneNumber(String areaCode, String number) { }\nrecord Country    (String countryCode, String countryName) { }\nrecord Passenger  (Name name,\n                   PhoneNumber phoneNumber,\n                   Country from,\n                   Country destination) { }\n\npublic class GeoMaps {\n    boolean checkFirstNameAndCountryCodeAgain (Object obj) {\n        if (obj instanceof Passenger(Name (String fName, _),\n                                     _,\n                                     _,\n                                     Country (String countryCode, _) )) {\n\n            if (fName != null &amp;&amp; countryCode != null) {\n                return fName.startsWith(&quot;Simo&quot;) &amp;&amp; countryCode.equals(&quot;PRG&quot;);\n            }\n        }\n        return false;\n    }\n}<\/pre>\n<h2>4. Looking forward &#8211; adding primitives in patterns and switch<\/h2>\n<p>Languages as we know, whether natural languages or programming languages, change and evolve. Moving forward, you would soon be able to use primitive type patterns in switch constructs. Targeted to be released as a preview feature in Java 23, Check out <a href=\"https:\/\/openjdk.org\/jeps\/455\" target=\"_blank\" rel=\"noopener\">this link<\/a> for further details.<\/p>\n<h2>Summary<\/h2>\n<p>The humble switch construct has come a long way with the introduction of switch expressions and pattern matching to it. They add clarity and conciseness to your code, which increases manifold when coupled with other new Java features such as the sealed classes, and coding practices like refactoring. Switch Expressions became and production feature in Java 14 and Pattern Matching and record patterns are a production feature since Java 21. You can use all these features in your codebase without any worries of them changing later.<\/p>\n<p>Watch out for further improvements to the switch construct via the unnamed patterns and addition of primitives to patterns.<\/p>\n<p>If you haven&#8217;t tried using the new evolved switch in your codebase, give it a try, you won&#8217;t regret it :-)<\/p>","protected":false},"author":921,"featured_media":439064,"comment_status":"closed","ping_status":"closed","template":"","categories":[4759,5088],"tags":[6743,6744,8323,3373,8325,8327],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/idea\/409395"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/idea"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/types\/idea"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/users\/921"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/comments?post=409395"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/idea\/409395\/revisions"}],"predecessor-version":[{"id":443368,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/idea\/409395\/revisions\/443368"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/media\/439064"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/media?parent=409395"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/categories?post=409395"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/tags?post=409395"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/cross-post-tag?post=409395"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}