[{"data":1,"prerenderedAt":110},["ShallowReactive",2],{"qa-\u002Fjava\u002Fmodern-java\u002Frecord-patterns":3},{"page":4,"siblings":78,"blog":107},{"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":69,"related":70,"seo":71,"seoDescription":72,"stem":73,"subtopic":6,"topic":74,"topicSlug":75,"updated":76,"__hash__":77},"qa\u002Fjava\u002Fmodern-java\u002Frecord-patterns.md","Record Patterns",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,7,"\u002Fjava\u002Fmodern-java\u002Frecord-patterns",[23,28,32,36,40,45,49,53,57,61,65],{"id":24,"difficulty":25,"q":26,"a":27},"what-are-record-patterns","easy","What are record patterns in Java?","**Record patterns** (Java 21, JEP 440) extend pattern matching to\n**destructure** record components directly in an `instanceof` or `switch`\nexpression, binding each component to a local variable in one step.\n\n```java\nrecord Point(int x, int y) {}\n\nObject obj = new Point(3, 4);\n\n\u002F\u002F Old way — instanceof check, then accessor calls:\nif (obj instanceof Point p) {\n    int x = p.x();\n    int y = p.y();\n    System.out.println(x + \", \" + y);\n}\n\n\u002F\u002F Record pattern — destructure inline:\nif (obj instanceof Point(int x, int y)) {\n    System.out.println(x + \", \" + y); \u002F\u002F x and y bound directly\n}\n```\n\n**Rule of thumb:** use record patterns when you need multiple components\nof a record — they eliminate the accessor boilerplate.\n",{"id":29,"difficulty":25,"q":30,"a":31},"record-patterns-in-switch","How do record patterns work in switch expressions?","Record patterns integrate seamlessly into switch type patterns, making\nmulti-branch dispatch over record-carrying sealed types concise:\n\n```java\nsealed interface Shape permits Circle, Rectangle {}\nrecord Circle(double radius)        implements Shape {}\nrecord Rectangle(double w, double h) implements Shape {}\n\ndouble area(Shape s) {\n    return switch (s) {\n        case Circle(double r)            -> Math.PI * r * r;\n        case Rectangle(double w, double h) -> w * h;\n    };\n}\n```\n\nThe components `r`, `w`, `h` are bound directly in each case — no\naccessor calls, no intermediate variables.\n\n**Rule of thumb:** record patterns in switch are the natural companion\nto sealed interfaces — together they model exhaustive dispatch over\ntyped data cleanly.\n",{"id":33,"difficulty":14,"q":34,"a":35},"nested-record-patterns","What are nested record patterns and when are they useful?","Record pattern components can themselves be patterns — you can destructure\na component that is another record, arbitrarily deep:\n\n```java\nrecord Point(int x, int y) {}\nrecord Line(Point start, Point end) {}\n\nObject obj = new Line(new Point(0, 0), new Point(3, 4));\n\nif (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {\n    double length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));\n    System.out.println(\"Length: \" + length); \u002F\u002F 5.0\n}\n```\n\nNested patterns are especially powerful for recursive data structures like\nexpression trees, JSON documents, or domain events.\n\n**Rule of thumb:** nested patterns eliminate deep chains of accessor calls\non nested records — write the shape of the data you expect, bind the values.\n",{"id":37,"difficulty":25,"q":38,"a":39},"var-in-record-patterns","Can you use var in a record pattern?","Yes. `var` infers the component type, which is useful when the type is\nlong or obvious from context:\n\n```java\nrecord Pair\u003CA, B>(A first, B second) {}\n\nObject obj = new Pair\u003C>(\"hello\", 42);\n\nif (obj instanceof Pair(var first, var second)) {\n    System.out.println(first + \" \" + second); \u002F\u002F \"hello 42\"\n    \u002F\u002F first is String, second is Integer — inferred\n}\n```\n\n`var` in a record pattern behaves the same as `var` in a local variable\ndeclaration — the type is fixed at compile time, just written shorter.\n\n**Rule of thumb:** use `var` in record patterns when the component types\nare verbose (e.g., `Map.Entry\u003CString, List\u003CInteger>>`) or clearly\napparent from context.\n",{"id":41,"difficulty":42,"q":43,"a":44},"record-patterns-with-generics","hard","How do generic records work with record patterns?","Record patterns support generic records, but due to **type erasure** the\ncomponent types must be compatible with the inferred erasure. Using\nexplicit types works; using concrete parameterisations may require a\ncast or `var`:\n\n```java\nrecord Box\u003CT>(T value) {}\n\nObject obj = new Box\u003C>(\"hello\");\n\n\u002F\u002F Using var — inferred as Object due to erasure:\nif (obj instanceof Box(var v)) {\n    System.out.println(v); \u002F\u002F \"hello\" — but v is typed as Object\n}\n\n\u002F\u002F Explicit type — works if you know the component type:\nif (obj instanceof Box(String s)) {\n    System.out.println(s.toUpperCase()); \u002F\u002F s is String — unchecked\n}\n```\n\nThe compiler may emit an unchecked warning for the explicit-type case\nbecause the generic argument is not verifiable at runtime (erasure).\n\n**Rule of thumb:** use `var` for generic record patterns to avoid\nunchecked warnings; add an explicit type only when you're certain of\nthe component's runtime type.\n",{"id":46,"difficulty":14,"q":47,"a":48},"guarded-record-patterns","Can you add a when guard to a record pattern in switch?","Yes. `when` guards work with record patterns just like with type patterns:\n\n```java\nsealed interface Expr permits Num, Add {}\nrecord Num(int v)           implements Expr {}\nrecord Add(Expr l, Expr r)  implements Expr {}\n\nString describe(Expr e) {\n    return switch (e) {\n        case Num(int v) when v \u003C 0   -> \"negative: \" + v;\n        case Num(int v) when v == 0  -> \"zero\";\n        case Num(int v)              -> \"positive: \" + v;\n        case Add(Expr l, Expr r)     -> \"sum of \" + describe(l) + \" and \" + describe(r);\n    };\n}\n```\n\n**Rule of thumb:** combine `when` with record patterns to filter on\ncomponent values without a nested `if` inside the case body.\n",{"id":50,"difficulty":14,"q":51,"a":52},"record-patterns-replace-visitor","How do record patterns help replace the Visitor pattern?","The Visitor pattern was a workaround for Java's lack of multi-dispatch and\nsealed type exhaustiveness. Record patterns + sealed interfaces + switch\nreplace it with far less ceremony:\n\n```java\n\u002F\u002F Visitor pattern — 30+ lines, double dispatch, accept\u002Fvisit boilerplate:\ninterface ExprVisitor\u003CR> {\n    R visitNum(Num n);\n    R visitAdd(Add a);\n}\n\u002F\u002F Each record implements accept(ExprVisitor\u003CR>)...\n\n\u002F\u002F Record patterns — same result, no infrastructure:\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\nString print(Expr e) {\n    return switch (e) {\n        case Num(int v)           -> String.valueOf(v);\n        case Add(Expr l, Expr r)  -> \"(\" + print(l) + \" + \" + print(r) + \")\";\n    };\n}\n```\n\nAdding a new operation (`eval`, `print`, `typecheck`) requires writing one\nnew function — no changes to existing classes, no new visitor interfaces.\n\n**Rule of thumb:** sealed + records + switch patterns = Visitor pattern\nwithout the boilerplate. Adding a new type forces you to update every switch\n(compile error); adding a new operation requires only a new function.\n",{"id":54,"difficulty":14,"q":55,"a":56},"record-patterns-instanceof-scope","What is the scope of variables bound in a record pattern instanceof?","Variables bound in a record pattern `instanceof` follow the same\nflow-sensitive scoping rules as simple type patterns — they are in scope\nonly in the branch where the match is guaranteed:\n\n```java\nrecord Point(int x, int y) {}\n\nObject obj = new Point(1, 2);\n\nif (obj instanceof Point(int x, int y)) {\n    System.out.println(x + \", \" + y); \u002F\u002F in scope here\n}\n\u002F\u002F x and y are NOT in scope here\n\n\u002F\u002F Also works with && guards:\nif (obj instanceof Point(int x, int y) && x > 0 && y > 0) {\n    System.out.println(\"first quadrant: \" + x + \", \" + y);\n}\n```\n\n**Rule of thumb:** record pattern variables behave exactly like simple\npattern variables — scoped to the true branch and `&&` chains.\n",{"id":58,"difficulty":25,"q":59,"a":60},"record-patterns-null","What happens when a record pattern is matched against null?","Like all `instanceof` patterns, a record pattern **does not match null** —\n`instanceof` returns `false` for null regardless of the pattern:\n\n```java\nrecord Point(int x, int y) {}\nObject obj = null;\n\nSystem.out.println(obj instanceof Point(int x, int y)); \u002F\u002F false\n\u002F\u002F x and y are never bound; no NPE\n\n\u002F\u002F In switch, null must be handled explicitly:\nswitch (obj) {\n    case null             -> System.out.println(\"null\");\n    case Point(int x, int y) -> System.out.println(x + \",\" + y);\n    default               -> System.out.println(\"other\");\n}\n```\n\n**Rule of thumb:** null never matches a record pattern — handle it with\n`case null` in switch or an explicit null check before `instanceof`.\n",{"id":62,"difficulty":14,"q":63,"a":64},"record-patterns-exhaustive","How do record patterns interact with exhaustiveness checking in switch?","Exhaustiveness checking applies to the **outer** type pattern, not to the\ninternal component structure. When the selector is a sealed type, the\ncompiler checks that all permitted subtypes are covered:\n\n```java\nsealed interface Shape permits Circle, Rect {}\nrecord Circle(double r)     implements Shape {}\nrecord Rect(double w, double h) implements Shape {}\n\n\u002F\u002F Exhaustive — all subtypes covered:\ndouble area(Shape s) {\n    return switch (s) {\n        case Circle(double r)          -> Math.PI * r * r;\n        case Rect(double w, double h)  -> w * h;\n    };\n}\n\u002F\u002F Adding a new Shape subtype → compile error here\n```\n\nThe compiler does not (currently) check exhaustiveness of component\nvalues — that's the role of `when` guards plus a `default` or catch-all.\n\n**Rule of thumb:** exhaustiveness in switch refers to which subtypes are\ncovered, not which component values — add `when` guards for component-level\nconditions and a final ungarded case as the catch-all.\n",{"id":66,"difficulty":25,"q":67,"a":68},"record-patterns-vs-accessor-chain","When should you use a record pattern versus calling accessors?","Use a **record pattern** when you need most or all components of a record:\n\n```java\n\u002F\u002F If you need both x and y — record pattern is cleaner:\nif (obj instanceof Point(int x, int y)) {\n    System.out.println(x + \", \" + y);\n}\n\n\u002F\u002F If you need only one component — accessor is simpler:\nif (obj instanceof Point p) {\n    System.out.println(p.x()); \u002F\u002F only x needed\n}\n```\n\nFor nested records where you'd need multiple accessor calls, the pattern\nis significantly more readable:\n\n```java\n\u002F\u002F Accessor chain — verbose:\nif (obj instanceof Line l) {\n    int x1 = l.start().x();\n    int y1 = l.start().y();\n    int x2 = l.end().x();\n    int y2 = l.end().y();\n    ...\n}\n\n\u002F\u002F Nested record pattern — concise:\nif (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {\n    ...\n}\n```\n\n**Rule of thumb:** record patterns pay off when you need multiple\ncomponents or when nesting is deep; stick to accessors for single-field\naccess.\n",11,null,{"description":11},"Java record patterns interview questions — deconstruction patterns, nested patterns, record patterns in instanceof, record patterns in switch, combining with sealed classes, var in patterns, and replacing instanceof chains.","java\u002Fmodern-java\u002Frecord-patterns","Modern Java","modern-java","2026-06-20","LY-WRR4LYHVxfjMQETGKTfLJWYOmwFd-4645hikOe2A",[79,83,86,90,94,98,102,103],{"subtopic":80,"path":81,"order":82},"Records","\u002Fjava\u002Fmodern-java\u002Frecords",1,{"subtopic":84,"path":85,"order":12},"Sealed Classes","\u002Fjava\u002Fmodern-java\u002Fsealed-classes",{"subtopic":87,"path":88,"order":89},"Switch Pattern Matching","\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching",3,{"subtopic":91,"path":92,"order":93},"Text Blocks","\u002Fjava\u002Fmodern-java\u002Ftext-blocks",4,{"subtopic":95,"path":96,"order":97},"instanceof Pattern Matching","\u002Fjava\u002Fmodern-java\u002Finstanceof-pattern-matching",5,{"subtopic":99,"path":100,"order":101},"Virtual Threads","\u002Fjava\u002Fmodern-java\u002Fvirtual-threads",6,{"subtopic":6,"path":21,"order":20},{"subtopic":104,"path":105,"order":106},"Sequenced Collections","\u002Fjava\u002Fmodern-java\u002Fsequenced-collections",8,{"path":108,"title":109},"\u002Fblog\u002Fjava-record-patterns","Java Record Patterns — Inline Deconstruction for Cleaner Switch Dispatch",1782244117705]