[{"data":1,"prerenderedAt":122},["ShallowReactive",2],{"qa-\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching":3},{"page":4,"siblings":90,"blog":119},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":81,"related":82,"seo":83,"seoDescription":84,"stem":85,"subtopic":6,"topic":86,"topicSlug":87,"updated":88,"__hash__":89},"qa\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching.md","Switch Pattern Matching",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,3,"\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching",[23,28,32,36,40,44,48,52,56,60,65,69,73,77],{"id":24,"difficulty":25,"q":26,"a":27},"switch-expression-vs-statement","easy","What is the difference between a switch statement and a switch expression?","A **switch statement** is an imperative control-flow construct that\nexecutes code for a matching case. A **switch expression** (Java 14,\nJEP 361) is an expression that **produces a value** and can be used\nanywhere an expression is expected.\n\n```java\n\u002F\u002F Switch statement — imperative, returns nothing\nString msg;\nswitch (day) {\n    case MONDAY: msg = \"start\"; break;\n    default:     msg = \"other\";\n}\n\n\u002F\u002F Switch expression — yields a value\nString msg = switch (day) {\n    case MONDAY -> \"start\";\n    default     -> \"other\";\n};\n```\n\nSwitch expressions use **arrow labels** (`->`) which don't fall through\nand don't need `break`. They must be **exhaustive** — every possible value\nmust be handled.\n\n**Rule of thumb:** prefer switch expressions over switch statements;\nthey're concise, don't fall through, and the compiler enforces exhaustiveness.\n",{"id":29,"difficulty":25,"q":30,"a":31},"arrow-vs-colon-syntax","What is the difference between arrow (→) and colon (:) case labels in switch?","**Arrow labels** (`case X ->`) execute a single expression, block, or\n`throw` and **do not fall through** to the next case.\n\n**Colon labels** (`case X:`) are the traditional style — they fall through\nto the next case unless a `break` or `return` stops them.\n\n```java\n\u002F\u002F Arrow — no fall-through, cleaner\nint result = switch (x) {\n    case 1 -> 10;\n    case 2 -> 20;\n    default -> 0;\n};\n\n\u002F\u002F Colon — falls through; needs break\nswitch (x) {\n    case 1:\n    case 2:\n        System.out.println(\"1 or 2\"); \u002F\u002F fall-through intentional here\n        break;\n    default:\n        System.out.println(\"other\");\n}\n```\n\nArrow labels can also use a block with `yield` to produce a value:\n```java\nint r = switch (x) {\n    case 1 -> 10;\n    default -> { int v = compute(x); yield v * 2; }\n};\n```\n\n**Rule of thumb:** use arrow labels for new switch expressions;\nuse colon labels only when intentional fall-through is needed.\n",{"id":33,"difficulty":25,"q":34,"a":35},"yield-keyword","What does the yield keyword do in a switch expression?","`yield` returns a value from a **block** arm of a switch expression,\nsimilar to `return` in a method but scoped to the switch:\n\n```java\nint fee = switch (membership) {\n    case \"gold\"   -> 0;\n    case \"silver\" -> 5;\n    default -> {\n        int base = 10;\n        int extra = membership.length();\n        yield base + extra;   \u002F\u002F produces the value of this arm\n    }\n};\n```\n\n`yield` is only valid inside a switch expression block arm; in arrow\narms the expression result is yielded implicitly.\n\n**Rule of thumb:** use `yield` only in block arms (`{ ... }`); single-\nexpression arms use the expression value automatically.\n",{"id":37,"difficulty":14,"q":38,"a":39},"type-pattern-in-switch","What is a type pattern in a switch expression?","A **type pattern** (Java 21, JEP 441) lets a `switch` match on the\nruntime type of the selector and bind the matched object to a variable in\none step — eliminating the cast:\n\n```java\nObject obj = getObject();\n\nString desc = switch (obj) {\n    case Integer i  -> \"int: \" + i;\n    case String s   -> \"string: \" + s;\n    case null       -> \"null\";\n    default         -> \"other: \" + obj.getClass().getSimpleName();\n};\n```\n\nCompare the old way:\n```java\n\u002F\u002F Before type patterns:\nif (obj instanceof Integer) {\n    Integer i = (Integer) obj;  \u002F\u002F explicit cast\n    ...\n}\n```\n\n**Rule of thumb:** type patterns in switch are cleaner than chains of\n`if\u002Felse instanceof` + cast — and they're exhaustiveness-checked when\nthe selector is a sealed type.\n",{"id":41,"difficulty":14,"q":42,"a":43},"guarded-patterns","What are guarded patterns (when clause) in switch?","A **guarded pattern** adds a boolean condition (`when`) to a type pattern\ncase, allowing fine-grained filtering without a nested `if`:\n\n```java\nObject obj = getValue();\n\nString result = switch (obj) {\n    case Integer i when i \u003C 0   -> \"negative int: \" + i;\n    case Integer i when i == 0  -> \"zero\";\n    case Integer i              -> \"positive int: \" + i;\n    case String s when s.isEmpty() -> \"empty string\";\n    case String s               -> \"string: \" + s;\n    default                     -> \"other\";\n};\n```\n\nThe `when` clause is evaluated only if the type pattern matches. Cases\nare evaluated **top-to-bottom**; more specific guards should come first.\n\n**Rule of thumb:** use `when` guards to avoid nested `if` inside a case\nblock; order from most to least specific so the right case is selected first.\n",{"id":45,"difficulty":14,"q":46,"a":47},"exhaustiveness-sealed","How does pattern matching enforce exhaustiveness for sealed types?","When the switch selector is a **sealed type**, the compiler verifies that\nevery permitted subtype is covered by at least one case. If you add a new\npermitted subtype without updating the switch, you get a **compile error**:\n\n```java\nsealed interface Shape permits Circle, Square {}\nrecord Circle(double r) implements Shape {}\nrecord Square(double s) implements Shape {}\n\ndouble area(Shape shape) {\n    return switch (shape) {    \u002F\u002F exhaustive — compiler verified\n        case Circle c -> Math.PI * c.r() * c.r();\n        case Square s -> s.s() * s.s();\n    };\n    \u002F\u002F If we add 'Triangle' to Shape's permits, this becomes a compile error\n}\n```\n\nFor non-sealed types or `Object`, a `default` case (or a `case null,\ndefault`) is required to make the switch exhaustive.\n\n**Rule of thumb:** sealed type + switch = compiler-enforced exhaustiveness;\n`default` is your escape hatch for open hierarchies.\n",{"id":49,"difficulty":14,"q":50,"a":51},"null-in-switch","How does switch handle null in Java 21?","Traditionally, passing `null` to a `switch` statement throws a\n`NullPointerException`. Java 21 allows explicit `case null` to handle it:\n\n```java\nString s = getString(); \u002F\u002F might be null\n\nswitch (s) {\n    case null           -> System.out.println(\"null!\");\n    case \"hello\"        -> System.out.println(\"hello\");\n    default             -> System.out.println(\"other: \" + s);\n}\n```\n\nYou can also combine null with default: `case null, default ->`.\nIf no `case null` is present, `null` still throws `NullPointerException`\nto preserve backward compatibility.\n\n**Rule of thumb:** add `case null` explicitly in pattern-matching switches\nwherever `null` is a possible input; don't rely on NPE as flow control.\n",{"id":53,"difficulty":14,"q":54,"a":55},"record-deconstruction-switch","How do record deconstruction patterns work in switch?","**Record patterns** (Java 21, JEP 440) destructure a record's components\ndirectly in a `case`, binding them to variables without explicit accessor\ncalls:\n\n```java\nsealed interface Expr permits Num, Add {}\nrecord Num(int v)           implements Expr {}\nrecord Add(Expr l, Expr r)  implements Expr {}\n\nint eval(Expr e) {\n    return switch (e) {\n        case Num(int v)           -> v;\n        case Add(Expr l, Expr r)  -> eval(l) + eval(r);\n    };\n}\n```\n\nNested deconstruction also works — you can deconstruct components that\nare themselves records:\n\n```java\nrecord Point(int x, int y) {}\nrecord Segment(Point start, Point end) {}\n\nString describe(Segment seg) {\n    return switch (seg) {\n        case Segment(Point(int x1, int y1), Point(int x2, int y2))\n            -> \"(%d,%d)→(%d,%d)\".formatted(x1, y1, x2, y2);\n    };\n}\n```\n\n**Rule of thumb:** record deconstruction removes the ceremony of\n`instanceof` + cast + accessor chain — especially powerful for recursive\ndata types like ASTs.\n",{"id":57,"difficulty":25,"q":58,"a":59},"pattern-matching-instanceof","What is pattern matching for instanceof and how does it relate to switch patterns?","**Pattern matching for `instanceof`** (Java 16, JEP 394) is the simpler\nsibling: it binds a variable in a single `instanceof` check:\n\n```java\nObject obj = getObject();\n\n\u002F\u002F Old way:\nif (obj instanceof String) {\n    String s = (String) obj;\n    System.out.println(s.toUpperCase());\n}\n\n\u002F\u002F Pattern matching (Java 16+):\nif (obj instanceof String s) {\n    System.out.println(s.toUpperCase()); \u002F\u002F s is in scope here\n}\n```\n\n`instanceof` patterns also work in logical expressions:\n```java\nif (obj instanceof String s && s.length() > 5) { ... }\n```\n\nSwitch type patterns (Java 21) extend the same concept to multi-way\ndispatch. Both are part of the same **pattern matching** feature family.\n\n**Rule of thumb:** use `instanceof` patterns for single-type checks;\nuse switch type patterns for multi-way dispatch over several types.\n",{"id":61,"difficulty":62,"q":63,"a":64},"dominance-ordering","hard","What is dominance in switch pattern matching and how do you order cases?","A type pattern `case A a` **dominates** `case B b` if every `B` is also\nan `A` (i.e., `B` is a subtype of `A`). The compiler rejects a switch\nwhere a more general pattern appears before a more specific one, because\nthe specific case would be **unreachable**:\n\n```java\nObject obj = ...;\n\n\u002F\u002F Compile error — 'case Object o' dominates 'case String s':\nswitch (obj) {\n    case Object o -> System.out.println(\"object\");\n    case String s -> System.out.println(\"string\"); \u002F\u002F unreachable!\n}\n\n\u002F\u002F Correct — most specific first:\nswitch (obj) {\n    case String s  -> System.out.println(\"string\");\n    case Integer i -> System.out.println(\"int\");\n    default        -> System.out.println(\"other\");\n}\n```\n\n**Rule of thumb:** order cases from most specific to most general;\nthe compiler will reject unreachable cases due to dominance.\n",{"id":66,"difficulty":25,"q":67,"a":68},"switch-strings-and-primitives","What types can be used as switch selectors in modern Java?","Switch selectors have expanded across Java versions:\n\n| Java version | Selector types |\n|---|---|\n| Traditional | `byte`, `short`, `char`, `int`, their wrappers, `String`, enums |\n| Java 14 (switch expressions) | Same types, new syntax |\n| Java 21 (pattern matching) | Any reference type (`Object`, interfaces, classes) |\n\n```java\n\u002F\u002F Java 21 — Object selector with type patterns:\nObject val = getValue();\nString desc = switch (val) {\n    case Integer i -> \"int \" + i;\n    case Long l    -> \"long \" + l;\n    case String s  -> \"str \" + s;\n    case null      -> \"null\";\n    default        -> \"?\" + val;\n};\n```\n\nNote: primitive `long`, `float`, `double` are **not** yet valid selectors\n(targeted for a future release via JEP 455 in Java 23 preview).\n\n**Rule of thumb:** in Java 21+ you can switch on any object — the old\nrestriction to `int`\u002F`String`\u002F`enum` is lifted for pattern matching.\n",{"id":70,"difficulty":25,"q":71,"a":72},"switch-multiple-labels","How do you match multiple values in a single switch case?","Both traditional and modern switch support **multiple labels per case**\nseparated by commas:\n\n```java\nint numLetters = switch (day) {\n    case MONDAY, FRIDAY, SUNDAY -> 6;\n    case TUESDAY               -> 7;\n    case THURSDAY, SATURDAY    -> 8;\n    case WEDNESDAY             -> 9;\n};\n```\n\nThis is cleaner than the old fall-through trick with colon syntax. In\npattern-matching switches, combining type patterns with multiple labels\nis limited — each type pattern must be a separate case.\n\n**Rule of thumb:** use comma-separated labels for grouping equivalent\nenum\u002Fconstant cases; keep type pattern cases separate.\n",{"id":74,"difficulty":25,"q":75,"a":76},"when-to-use-switch-expression","When should you prefer a switch expression over an if-else chain?","Prefer a **switch expression** when:\n- You're selecting one of several branches based on a single value.\n- The result is a value assigned to a variable or returned.\n- You want the compiler to enforce exhaustiveness.\n- The type is an enum, sealed type, or set of known constants.\n\nPrefer **if-else** when:\n- Conditions are complex boolean expressions (not just equality).\n- You're testing different variables in each branch.\n- Only one or two branches exist (switch adds noise for binary choices).\n\n```java\n\u002F\u002F Good switch expression — enum selector, value result, exhaustive\ndouble discount = switch (tier) {\n    case GOLD   -> 0.20;\n    case SILVER -> 0.10;\n    case BRONZE -> 0.05;\n};\n\n\u002F\u002F Keep as if-else — unrelated conditions\nif (user.isAdmin() && request.getSize() > MAX) { ... }\nelse if (ctx.isReadOnly()) { ... }\n```\n\n**Rule of thumb:** switch expressions shine on enums and sealed types;\nif-else is better for unrelated conditions or complex predicates.\n",{"id":78,"difficulty":14,"q":79,"a":80},"switch-vs-polymorphism","When should you use switch pattern matching versus polymorphism?","This is the classic **tell-don't-ask** debate:\n\n**Polymorphism** is better when:\n- You expect third parties to add new subtypes (open hierarchy).\n- The operation is fundamental to the type and belongs in the class.\n- You have many operations that all need the same subtype-specific behaviour.\n\n**Switch pattern matching** is better when:\n- You own all the subtypes (closed\u002Fsealed hierarchy).\n- The operation is external to the type (e.g., serialisation, rendering).\n- You want compile-time exhaustiveness checking.\n\n```java\n\u002F\u002F Polymorphism — Shape knows how to compute its own area\ninterface Shape { double area(); }\n\n\u002F\u002F Switch — external function; exhaustiveness verified by compiler\ndouble area(Shape s) {\n    return switch (s) {\n        case Circle c    -> Math.PI * c.r() * c.r();\n        case Rectangle r -> r.w() * r.h();\n    };\n}\n```\n\n**Rule of thumb:** sealed types + switch for operations owned by the caller;\npolymorphism for behaviour the type itself should own.\n",14,null,{"description":11},"Java switch pattern matching interview questions — switch expressions vs statements, type patterns, guarded patterns, record deconstruction, exhaustiveness, null handling in switch, arrow vs colon syntax, and pattern matching for instanceof.","java\u002Fmodern-java\u002Fswitch-pattern-matching","Modern Java","modern-java","2026-06-20","GAwpTTmi-7w5m78Th5Ygzha-X_UEF8pxOyLiB6lfN2w",[91,95,98,99,103,107,111,115],{"subtopic":92,"path":93,"order":94},"Records","\u002Fjava\u002Fmodern-java\u002Frecords",1,{"subtopic":96,"path":97,"order":12},"Sealed Classes","\u002Fjava\u002Fmodern-java\u002Fsealed-classes",{"subtopic":6,"path":21,"order":20},{"subtopic":100,"path":101,"order":102},"Text Blocks","\u002Fjava\u002Fmodern-java\u002Ftext-blocks",4,{"subtopic":104,"path":105,"order":106},"instanceof Pattern Matching","\u002Fjava\u002Fmodern-java\u002Finstanceof-pattern-matching",5,{"subtopic":108,"path":109,"order":110},"Virtual Threads","\u002Fjava\u002Fmodern-java\u002Fvirtual-threads",6,{"subtopic":112,"path":113,"order":114},"Record Patterns","\u002Fjava\u002Fmodern-java\u002Frecord-patterns",7,{"subtopic":116,"path":117,"order":118},"Sequenced Collections","\u002Fjava\u002Fmodern-java\u002Fsequenced-collections",8,{"path":120,"title":121},"\u002Fblog\u002Fjava-switch-pattern-matching","Java Switch Pattern Matching — Expressions, Type Patterns, and Record Deconstruction",1782244117582]