{"id":"sealed-and-patterns-records","slug":"record-patterns","title":"Record patterns","text":"A type pattern gives you the whole object, and you then read its values with accessors. Since Java 21, a pattern can also take a record apart: a **record pattern** names the record and, in parentheses, a pattern for each of its components.\n\n### Taking a record apart\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(receipt(new Cash(450)));\n        System.out.println(receipt(new BankCard(\"4921\", 1250)));\n        System.out.println(receipt(new Voucher(\"SPRING10\", 1000)));\n    }\n\n    public static String receipt(Payment payment) {\n        return switch (payment) {\n            case Cash(int cents) -> \"Cash: \" + cents + \" cents\";\n            case BankCard(String digits, int cents) -> \"Card ending \" + digits + \": \" + cents + \" cents\";\n            case Voucher(String code, int cents) -> \"Voucher \" + code + \": \" + cents + \" cents\";\n        };\n    }\n}\n// ==== Payment.java ====\npublic sealed interface Payment permits Cash, BankCard, Voucher {\n}\n// ==== Cash.java ====\npublic record Cash(int cents) implements Payment {\n}\n// ==== BankCard.java ====\npublic record BankCard(String lastDigits, int cents) implements Payment {\n}\n// ==== Voucher.java ====\npublic record Voucher(String code, int cents) implements Payment {\n}\n```\n\n```output\nCash: 450 cents\nCard ending 4921: 1250 cents\nVoucher SPRING10: 1000 cents\n```\n\n`BankCard(String digits, int cents)` matches a BankCard, and declares a variable for each component, filled in from the accessors: `digits` gets the value of `lastDigits()`, and `cents` of `cents()`.\n\n- The components are matched **by their position**, not by name: the first variable gets the first component. The names are yours to choose, as `digits` shows.\n- The types must fit the components' types. In place of a type, `var` (module 57) lets javac take the component's own type: `case BankCard(var digits, var cents)`.\n- Every component gets a pattern, even one the case doesn't use.\n\nRecord patterns work after `instanceof` too, and can be combined with `&&` like type patterns:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Payment[] today = {new Voucher(\"SPRING10\", 1000), new Cash(450), new Voucher(\"BIG50\", 5000)};\n        for (Payment payment : today) {\n            if (payment instanceof Voucher(String code, int cents) && cents >= 5000) {\n                System.out.println(\"Check the voucher \" + code + \" by hand\");\n            }\n        }\n    }\n}\n// ==== Payment.java ====\npublic sealed interface Payment permits Cash, BankCard, Voucher {\n}\n// ==== Cash.java ====\npublic record Cash(int cents) implements Payment {\n}\n// ==== BankCard.java ====\npublic record BankCard(String lastDigits, int cents) implements Payment {\n}\n// ==== Voucher.java ====\npublic record Voucher(String code, int cents) implements Payment {\n}\n```\n\n```output\nCheck the voucher BIG50 by hand\n```\n\nIn a switch, a `when` guard can use the pattern's variables the same way. The guarded case comes before the plain one, so a big voucher stops at the first case:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Payment[] today = {new Voucher(\"SPRING10\", 1000), new Cash(450), new Voucher(\"BIG50\", 5000)};\n        for (Payment payment : today) {\n            System.out.println(describe(payment));\n        }\n    }\n\n    public static String describe(Payment payment) {\n        return switch (payment) {\n            case Voucher(String code, int cents) when cents >= 5000 -> \"Check the voucher \" + code + \" by hand\";\n            case Voucher(String code, int cents) -> \"Voucher \" + code;\n            case Cash(int cents) -> \"Cash\";\n            case BankCard(String digits, int cents) -> \"Card \" + digits;\n        };\n    }\n}\n// ==== Payment.java ====\npublic sealed interface Payment permits Cash, BankCard, Voucher {\n}\n// ==== Cash.java ====\npublic record Cash(int cents) implements Payment {\n}\n// ==== BankCard.java ====\npublic record BankCard(String lastDigits, int cents) implements Payment {\n}\n// ==== Voucher.java ====\npublic record Voucher(String code, int cents) implements Payment {\n}\n```\n\n```output\nVoucher SPRING10\nCash\nCheck the voucher BIG50 by hand\n```\n\n### Patterns inside patterns\n\nA component's pattern can itself be a record pattern or a type pattern. A café order holds the item and its payment, and one switch can look at both at once:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(note(new Order(\"cake\", new Voucher(\"SPRING10\", 1000))));\n        System.out.println(note(new Order(\"tea\", new BankCard(\"4921\", 320))));\n        System.out.println(note(new Order(\"bun\", new Cash(250))));\n    }\n\n    public static String note(Order order) {\n        return switch (order) {\n            case Order(String item, Voucher(String code, int cents)) -> item + \" paid with voucher \" + code;\n            case Order(String item, BankCard(var digits, var cents)) -> item + \" paid with card \" + digits;\n            case Order(String item, Payment other) -> item + \" paid\";\n        };\n    }\n}\n// ==== Order.java ====\npublic record Order(String item, Payment payment) {\n}\n// ==== Payment.java ====\npublic sealed interface Payment permits Cash, BankCard, Voucher {\n}\n// ==== Cash.java ====\npublic record Cash(int cents) implements Payment {\n}\n// ==== BankCard.java ====\npublic record BankCard(String lastDigits, int cents) implements Payment {\n}\n// ==== Voucher.java ====\npublic record Voucher(String code, int cents) implements Payment {\n}\n```\n\n```output\ncake paid with voucher SPRING10\ntea paid with card 4921\nbun paid\n```\n\nThe last case, `Order(String item, Payment other)`, matches every order, so the switch is exhaustive.\n\n### Records that hold their own kind\n\nA record of a sealed interface can have a component of that same interface. Then one object can hold another, which holds another, and a method that takes them apart calls itself (module 55). A present can be a gift, or something wrapped in paper around another present:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Present box = new Wrapped(\"red\", new Wrapped(\"gold\", new Gift(\"a scarf\")));\n        System.out.println(unwrap(box));\n    }\n\n    public static String unwrap(Present present) {\n        return switch (present) {\n            case Gift(String name) -> name;\n            case Wrapped(String paper, Present inside) -> paper + \" paper, then \" + unwrap(inside);\n        };\n    }\n}\n// ==== Present.java ====\npublic sealed interface Present permits Gift, Wrapped {\n}\n// ==== Gift.java ====\npublic record Gift(String name) implements Present {\n}\n// ==== Wrapped.java ====\npublic record Wrapped(String paper, Present inside) implements Present {\n}\n```\n\n```output\nred paper, then gold paper, then a scarf\n```\n\nThe base case is the Gift, which holds no other present. The last challenge of this module builds a calculator on the same idea.\n","task":"A floor plan file has been read into a list of objects: a String for each room's name, and a `Corner(int x, int y)` record for each corner of the walls, in centimeters. Write `printCorners(ArrayList<Object> items)`, which prints each corner as `corner at x, y` and skips everything else. Take the corner apart with a record pattern after `instanceof`, without calling the accessors. The check runs:\n\n```java\nArrayList<Object> items = new ArrayList<>();\nitems.add(\"Kitchen\");\nitems.add(new Corner(0, 0));\nitems.add(new Corner(400, 0));\nitems.add(\"Hall\");\nitems.add(new Corner(400, 350));\nprintCorners(items);\n```\n\nand expects:\n\n```\ncorner at 0, 0\ncorner at 400, 0\ncorner at 400, 350\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Object> items = new ArrayList<>();\n        items.add(\"Kitchen\");\n        items.add(new Corner(0, 0));\n        items.add(new Corner(400, 0));\n        items.add(\"Hall\");\n        items.add(new Corner(400, 350));\n        printCorners(items);\n    }\n\n    public static void printCorners(ArrayList<Object> items) {\n\n    }\n}\n\n// ==== Corner.java ====\npublic record Corner(int x, int y) {\n}\n","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Object> items = new ArrayList<>();\n        items.add(\"Kitchen\");\n        items.add(new Corner(0, 0));\n        items.add(new Corner(400, 0));\n        items.add(\"Hall\");\n        items.add(new Corner(400, 350));\n        printCorners(items);\n    }\n\n    public static void printCorners(ArrayList<Object> items) {\n        for (Object item : items) {\n            if (item instanceof Corner(int x, int y)) {\n                System.out.println(\"corner at \" + x + \", \" + y);\n            }\n        }\n    }\n}\n\n// ==== Corner.java ====\npublic record Corner(int x, int y) {\n}\n","hints":["Go through the list with `for (Object item : items)`.","`if (item instanceof Corner(int x, int y))` matches the corners only, and gives you `x` and `y` to print."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Object> items = new ArrayList<>();\nitems.add(\"Kitchen\");\nitems.add(new Corner(0, 0));\nitems.add(new Corner(400, 0));\nitems.add(\"Hall\");\nitems.add(new Corner(400, 350));\nprintCorners(items);\n","expect":"corner at 0, 0\ncorner at 400, 0\ncorner at 400, 350","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Object> items = new ArrayList<>();\nitems.add(new Corner(-50, 120));\nitems.add(\"Attic\");\nitems.add(42);\nitems.add(new Corner(7, 7));\nprintCorners(items);\nSystem.out.println(\"done\");\n","expect":"corner at -50, 120\ncorner at 7, 7\ndone","hidden":true}],"require":[{"pattern":"instanceof\\s+Corner\\s*\\(","message":"Use a record pattern after `instanceof`: `item instanceof Corner(int x, int y)`."}],"forbid":[{"pattern":"\\.\\s*(x|y)\\s*\\(\\s*\\)","message":"Take the corner apart with the record pattern instead of calling `x()` and `y()`."}],"more":[{"task":"Each wall of the floor plan goes from one corner to another: `Wall(Corner from, Corner to)`. Write `describe(Wall wall)`, which returns:\n\n- `horizontal, ` and the length and ` cm`, when both corners have the same `y`;\n- `vertical, ` and the length and ` cm`, when both corners have the same `x` (and different `y`s);\n- `slanted` otherwise.\n\nThe length is the difference of the coordinates that change, as a positive number (`Math.abs`). Take the wall and both its corners apart with one nested record pattern, `Wall(Corner(...), Corner(...))`, in a switch or after `instanceof`, without calling the accessors. The check runs:\n\n```java\nSystem.out.println(describe(new Wall(new Corner(0, 0), new Corner(400, 0))));\nSystem.out.println(describe(new Wall(new Corner(400, 350), new Corner(400, 0))));\nSystem.out.println(describe(new Wall(new Corner(0, 0), new Corner(300, 200))));\n```\n\nand expects:\n\n```\nhorizontal, 400 cm\nvertical, 350 cm\nslanted\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(describe(new Wall(new Corner(0, 0), new Corner(400, 0))));\n        System.out.println(describe(new Wall(new Corner(400, 350), new Corner(400, 0))));\n        System.out.println(describe(new Wall(new Corner(0, 0), new Corner(300, 200))));\n    }\n\n    public static String describe(Wall wall) {\n        return \"\";\n    }\n}\n\n// ==== Wall.java ====\npublic record Wall(Corner from, Corner to) {\n}\n\n// ==== Corner.java ====\npublic record Corner(int x, int y) {\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(describe(new Wall(new Corner(0, 0), new Corner(400, 0))));\n        System.out.println(describe(new Wall(new Corner(400, 350), new Corner(400, 0))));\n        System.out.println(describe(new Wall(new Corner(0, 0), new Corner(300, 200))));\n    }\n\n    public static String describe(Wall wall) {\n        return switch (wall) {\n            case Wall(Corner(int x1, int y1), Corner(int x2, int y2)) when y1 == y2 -> \"horizontal, \" + Math.abs(x2 - x1) + \" cm\";\n            case Wall(Corner(int x1, int y1), Corner(int x2, int y2)) when x1 == x2 -> \"vertical, \" + Math.abs(y2 - y1) + \" cm\";\n            case Wall other -> \"slanted\";\n        };\n    }\n}\n\n// ==== Wall.java ====\npublic record Wall(Corner from, Corner to) {\n}\n\n// ==== Corner.java ====\npublic record Corner(int x, int y) {\n}\n","hints":["`Wall(Corner(int x1, int y1), Corner(int x2, int y2))` gives all four coordinates at once. It can be a case of `switch (wall)`, with `when y1 == y2` for horizontal walls.","In a switch, end with a case that matches every wall, such as `case Wall other -> \"slanted\";`. With `instanceof`, use the four variables in ifs inside it."],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(describe(new Wall(new Corner(0, 0), new Corner(400, 0))));\nSystem.out.println(describe(new Wall(new Corner(400, 350), new Corner(400, 0))));\nSystem.out.println(describe(new Wall(new Corner(0, 0), new Corner(300, 200))));\n","expect":"horizontal, 400 cm\nvertical, 350 cm\nslanted","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(describe(new Wall(new Corner(900, 120), new Corner(250, 120))));\nSystem.out.println(describe(new Wall(new Corner(-30, 10), new Corner(-30, 75))));\nSystem.out.println(describe(new Wall(new Corner(5, 5), new Corner(6, 4))));\n","expect":"horizontal, 650 cm\nvertical, 65 cm\nslanted","hidden":true}],"require":[{"pattern":"\\bWall\\s*\\(\\s*Corner\\s*\\(","message":"Take the wall apart with a nested record pattern: `Wall(Corner(int x1, int y1), Corner(int x2, int y2))`."}],"forbid":[{"pattern":"\\.\\s*(x|y|from|to)\\s*\\(\\s*\\)","message":"Use the variables of the record pattern instead of calling the accessors."}]},{"task":"A small calculator stores an expression such as `(2 + 3) * 4` as a tree of records. An `Expr` is a number, `Num(int value)`, or the sum or the product of two smaller expressions, `Add(Expr left, Expr right)` and `Mul(Expr left, Expr right)`. The method `show` in `Main.java` already turns an expression into text, with a switch over record patterns that calls itself for the parts. Write `eval(Expr expr)` in the same way: it returns the expression's value. Use a case with a record pattern for each of the three records, and no `default`. The check runs:\n\n```java\nExpr sum = new Add(new Num(2), new Num(3));\nExpr product = new Mul(sum, new Num(4));\nSystem.out.println(show(sum) + \" = \" + eval(sum));\nSystem.out.println(show(product) + \" = \" + eval(product));\n```\n\nand expects:\n\n```\n(2 + 3) = 5\n((2 + 3) * 4) = 20\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Expr sum = new Add(new Num(2), new Num(3));\n        Expr product = new Mul(sum, new Num(4));\n        System.out.println(show(sum) + \" = \" + eval(sum));\n        System.out.println(show(product) + \" = \" + eval(product));\n    }\n\n    public static String show(Expr expr) {\n        return switch (expr) {\n            case Num(int value) -> String.valueOf(value);\n            case Add(Expr left, Expr right) -> \"(\" + show(left) + \" + \" + show(right) + \")\";\n            case Mul(Expr left, Expr right) -> \"(\" + show(left) + \" * \" + show(right) + \")\";\n        };\n    }\n\n    public static int eval(Expr expr) {\n        return 0;\n    }\n}\n\n// ==== Expr.java ====\npublic sealed interface Expr permits Num, Add, Mul {\n}\n\n// ==== Num.java ====\npublic record Num(int value) implements Expr {\n}\n\n// ==== Add.java ====\npublic record Add(Expr left, Expr right) implements Expr {\n}\n\n// ==== Mul.java ====\npublic record Mul(Expr left, Expr right) implements Expr {\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Expr sum = new Add(new Num(2), new Num(3));\n        Expr product = new Mul(sum, new Num(4));\n        System.out.println(show(sum) + \" = \" + eval(sum));\n        System.out.println(show(product) + \" = \" + eval(product));\n    }\n\n    public static String show(Expr expr) {\n        return switch (expr) {\n            case Num(int value) -> String.valueOf(value);\n            case Add(Expr left, Expr right) -> \"(\" + show(left) + \" + \" + show(right) + \")\";\n            case Mul(Expr left, Expr right) -> \"(\" + show(left) + \" * \" + show(right) + \")\";\n        };\n    }\n\n    public static int eval(Expr expr) {\n        return switch (expr) {\n            case Num(int value) -> value;\n            case Add(Expr left, Expr right) -> eval(left) + eval(right);\n            case Mul(Expr left, Expr right) -> eval(left) * eval(right);\n        };\n    }\n}\n\n// ==== Expr.java ====\npublic sealed interface Expr permits Num, Add, Mul {\n}\n\n// ==== Num.java ====\npublic record Num(int value) implements Expr {\n}\n\n// ==== Add.java ====\npublic record Add(Expr left, Expr right) implements Expr {\n}\n\n// ==== Mul.java ====\npublic record Mul(Expr left, Expr right) implements Expr {\n}\n","hints":["Copy the shape of `show`: `return switch (expr) { ... };` with the same three case labels.","A Num's value is its `value`. For an Add, evaluate both parts and add them: `eval(left) + eval(right)`. A Mul multiplies them."],"tests":[{"name":"Test 1","stdin":"","call":"Expr sum = new Add(new Num(2), new Num(3));\nExpr product = new Mul(sum, new Num(4));\nSystem.out.println(show(sum) + \" = \" + eval(sum));\nSystem.out.println(show(product) + \" = \" + eval(product));\n","expect":"(2 + 3) = 5\n((2 + 3) * 4) = 20","hidden":false},{"name":"Test 2","stdin":"","call":"Expr a = new Mul(new Num(-3), new Add(new Num(7), new Mul(new Num(2), new Num(5))));\nSystem.out.println(show(a) + \" = \" + eval(a));\nSystem.out.println(eval(new Num(42)));\nExpr big = new Num(1);\nfor (int i = 2; i <= 6; i++) {\n    big = new Mul(big, new Num(i));\n}\nSystem.out.println(show(big) + \" = \" + eval(big));\n","expect":"(-3 * (7 + (2 * 5))) = -51\n42\n(((((1 * 2) * 3) * 4) * 5) * 6) = 720","hidden":true}],"require":[{"pattern":"\\bcase\\s+Num\\s*\\(","min":2,"message":"Give eval a case with a record pattern for Num: `case Num(int value) -> ...`."},{"pattern":"\\bcase\\s+Add\\s*\\(","min":2,"message":"Give eval a case with a record pattern for Add: `case Add(Expr left, Expr right) -> ...`."},{"pattern":"\\bcase\\s+Mul\\s*\\(","min":2,"message":"Give eval a case with a record pattern for Mul: `case Mul(Expr left, Expr right) -> ...`."}],"forbid":[{"pattern":"\\bdefault\\b","message":"Leave out `default`: Expr is sealed, and the three cases cover it."},{"pattern":"\\binstanceof\\b","message":"Use a switch with record patterns instead of `instanceof`."}]}]}