[{"data":1,"prerenderedAt":566},["ShallowReactive",2],{"topic-java-modern-java":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fjava.yml","Java interview questions on language fundamentals, object-oriented design, the Collections Framework, exception handling and concurrency — a staple of backend and enterprise technical interviews.","yml","java",{},"Java",5,"frameworks\u002Fjava",1,"jcUKEcQDoMZrreMRtZYJIVpkQlB-varlzbuwmzLr7kc",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fjava-modern-java.yml","Records, sealed classes and pattern matching for switch — the language features from Java 14+ that make recent Java more concise and expressive.",{},"Modern Java",9,"modern-java","topics\u002Fjava-modern-java","39pOVSqSYZr8fjGosC8uuiD9QnIqHf40JmGYx0V6has",[25,109,181,252,316,379,447,507],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":102,"related":103,"seo":104,"seoDescription":105,"stem":106,"subtopic":27,"topic":19,"topicSlug":21,"updated":107,"__hash__":108},"qa\u002Fjava\u002Fmodern-java\u002Frecords.md","Records",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fjava\u002Fmodern-java\u002Frecords",[41,46,50,54,58,62,66,70,74,78,82,86,90,94,98],{"id":42,"difficulty":43,"q":44,"a":45},"what-is-a-record","easy","What is a record in Java and what problem does it solve?","A **record** (Java 16, JEP 395) is a special class declaration for\n**immutable data carriers**. It eliminates the boilerplate of writing\nconstructors, `equals()`, `hashCode()`, `toString()`, and accessor methods\nfor classes whose sole purpose is to hold data.\n\n```java\n\u002F\u002F Before records — ~30 lines of boilerplate:\npublic final class Point {\n    private final int x;\n    private final int y;\n    public Point(int x, int y) { this.x = x; this.y = y; }\n    public int x() { return x; }\n    public int y() { return y; }\n    @Override public boolean equals(Object o) { ... }\n    @Override public int hashCode() { ... }\n    @Override public String toString() { ... }\n}\n\n\u002F\u002F With a record — 1 line:\nrecord Point(int x, int y) {}\n```\n\n**Rule of thumb:** use a record whenever a class exists purely to group\nrelated data; keep domain logic in regular classes.\n",{"id":47,"difficulty":43,"q":48,"a":49},"record-components","What are record components and what does the compiler generate from them?","The identifiers in the record header are called **components**. For each\ncomponent the compiler automatically generates:\n\n- A `private final` field with the same name and type.\n- A **public accessor method** with the same name as the component (not\n  `getX()` — just `x()`).\n- A **canonical constructor** that accepts all components and assigns them.\n- `equals()`, `hashCode()`, and `toString()` based on all components.\n\n```java\nrecord Person(String name, int age) {}\n\nPerson p = new Person(\"Alice\", 30);\nSystem.out.println(p.name());     \u002F\u002F \"Alice\"  — accessor\nSystem.out.println(p.age());      \u002F\u002F 30\nSystem.out.println(p);            \u002F\u002F Person[name=Alice, age=30]\nPerson p2 = new Person(\"Alice\", 30);\nSystem.out.println(p.equals(p2)); \u002F\u002F true — all components equal\n```\n\n**Rule of thumb:** record accessors use the component name directly —\n`point.x()` not `point.getX()`.\n",{"id":51,"difficulty":43,"q":52,"a":53},"record-immutability","Are records immutable? What are the caveats?","Record fields are **`private final`**, so the reference cannot be\nreassigned after construction. However, if a component type is mutable\n(e.g., `List`, array), the object it refers to can still be mutated.\n\n```java\nrecord Scores(List\u003CInteger> values) {}\n\nScores s = new Scores(new ArrayList\u003C>(List.of(1, 2, 3)));\ns.values().add(99); \u002F\u002F legal — mutates the list inside the record\nSystem.out.println(s.values()); \u002F\u002F [1, 2, 3, 99]\n```\n\nFor true deep immutability, wrap mutable components with\n`Collections.unmodifiableList()` or copy them defensively in the\ncanonical constructor.\n\n**Rule of thumb:** records give shallow immutability; make components\ndeeply immutable yourself if needed.\n",{"id":55,"difficulty":35,"q":56,"a":57},"canonical-constructor","What is a canonical constructor and how can you customise it?","The **canonical constructor** has the same parameter list as the record\nheader. The compiler generates one automatically, but you can override it\nto add validation or normalisation:\n\n```java\nrecord Range(int min, int max) {\n    Range {                          \u002F\u002F compact canonical constructor\n        if (min > max)\n            throw new IllegalArgumentException(\"min > max\");\n        \u002F\u002F no need to write: this.min = min; this.max = max;\n        \u002F\u002F the compiler appends the assignments automatically\n    }\n}\n\nnew Range(1, 10); \u002F\u002F ok\nnew Range(10, 1); \u002F\u002F IllegalArgumentException\n```\n\nThe **compact constructor** (no parameter list, no `this.x = x` assignments)\nis the preferred style — the compiler appends the field assignments after\nyour body.\n\n**Rule of thumb:** use the compact canonical constructor for validation and\nnormalisation; let the compiler handle the field assignments.\n",{"id":59,"difficulty":35,"q":60,"a":61},"custom-constructor-in-record","Can a record have additional constructors beyond the canonical one?","Yes. A record can define **alternative constructors** that must delegate to\nthe canonical constructor as their first statement (like `this(...)`).\n\n```java\nrecord Point(double x, double y) {\n    Point() { this(0.0, 0.0); }              \u002F\u002F defaults to origin\n    Point(double x) { this(x, 0.0); }        \u002F\u002F y defaults to 0\n}\n\nPoint origin = new Point();   \u002F\u002F (0.0, 0.0)\nPoint onAxis = new Point(3.0); \u002F\u002F (3.0, 0.0)\n```\n\n**Rule of thumb:** alternative constructors must chain to the canonical\nconstructor — the canonical constructor is always the \"source of truth\" for\nfield assignment.\n",{"id":63,"difficulty":35,"q":64,"a":65},"record-vs-class","What are the restrictions that make records different from regular classes?","Records have several compiler-enforced restrictions:\n\n- **Implicitly `final`** — cannot be subclassed.\n- **Cannot extend** any class (implicitly extends `java.lang.Record`).\n- **Cannot declare instance fields** beyond the components (static fields are allowed).\n- Accessor, `equals`, `hashCode`, and `toString` can be overridden but must\n  stay consistent with component semantics.\n- **Cannot be abstract**.\n\n```java\nrecord Pair\u003CA, B>(A first, B second) {}  \u002F\u002F generic records: fine\n\u002F\u002F record MyRecord(...) extends SomeClass {} \u002F\u002F compile error\n\u002F\u002F class Child extends Pair {}              \u002F\u002F compile error — records are final\n```\n\n**Rule of thumb:** if you need inheritance or mutable state, use a regular\nclass; use a record only when the class is a pure data carrier.\n",{"id":67,"difficulty":35,"q":68,"a":69},"record-implements-interface","Can a record implement an interface?","Yes. Records can implement any number of interfaces, including providing\ncustom implementations of the interface's methods.\n\n```java\ninterface Printable { void print(); }\n\nrecord Invoice(String id, double amount) implements Printable {\n    @Override\n    public void print() {\n        System.out.printf(\"Invoice %s: $%.2f%n\", id, amount);\n    }\n}\n\nnew Invoice(\"INV-001\", 199.99).print(); \u002F\u002F Invoice INV-001: $199.99\n```\n\nRecords are commonly used with `Comparable`, `Serializable`, or custom\ndomain interfaces.\n\n**Rule of thumb:** records implement interfaces freely; they just can't\nextend a class.\n",{"id":71,"difficulty":35,"q":72,"a":73},"record-vs-lombok","How do records compare to Lombok's @Data or @Value?","| Aspect | Java Record | Lombok `@Value` \u002F `@Data` |\n|---|---|---|\n| Accessor style | `x()` | `getX()` (JavaBean convention) |\n| Immutability | Enforced by compiler | `@Value` enforces, `@Data` does not |\n| Inheritance | Cannot extend or be extended | Normal class inheritance |\n| Build tool dependency | None — JDK feature | Requires Lombok on classpath and annotation processor |\n| Customisation | Compact constructor | `@Builder`, `@NonNull`, etc. |\n\n```java\n\u002F\u002F Lombok @Value:\n@Value public class Point { int x; int y; }\npoint.getX(); \u002F\u002F JavaBean getter\n\n\u002F\u002F Java record:\nrecord Point(int x, int y) {}\npoint.x();    \u002F\u002F component accessor\n```\n\nRecords are the language-native solution; Lombok is more flexible for\nlegacy codebases that need `getX()` style or mutable builders.\n\n**Rule of thumb:** prefer records for new code on Java 16+; use Lombok when\nyou need `getX()` compatibility with frameworks like Jackson (before proper\nrecord support), or when you need mutable builders.\n",{"id":75,"difficulty":35,"q":76,"a":77},"records-with-jackson","How do you use records with Jackson for JSON serialisation?","Jackson supports records natively since **Jackson 2.12**. It serialises\nusing the component accessors and deserialises using the canonical\nconstructor. No extra annotations are needed for simple cases:\n\n```java\nrecord User(String name, int age) {}\n\nObjectMapper mapper = new ObjectMapper();\nString json = mapper.writeValueAsString(new User(\"Alice\", 30));\n\u002F\u002F {\"name\":\"Alice\",\"age\":30}\n\nUser u = mapper.readValue(json, User.class);\n\u002F\u002F User[name=Alice, age=30]\n```\n\nFor constructor parameter mapping on older Jackson versions or when\ncomponent names don't match JSON keys, add\n`@JsonProperty` on the compact constructor parameters.\n\n**Rule of thumb:** Jackson 2.12+ handles records out of the box; for\nolder versions add `jackson-module-parameter-names` and\n`-parameters` compiler flag.\n",{"id":79,"difficulty":35,"q":80,"a":81},"records-in-switch","How do records interact with pattern matching in switch expressions?","Records are first-class citizens of Java's **deconstruction patterns**\n(Java 21). You can destructure a record's components directly in a\n`switch` or `instanceof` pattern:\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 are bound as local variables in the case body without an\nexplicit cast or accessor call.\n\n**Rule of thumb:** records + sealed interfaces + switch pattern matching\nwork together as a modern alternative to the visitor pattern.\n",{"id":83,"difficulty":35,"q":84,"a":85},"record-serialisation","How do records behave with Java serialisation?","Records support Java serialisation natively when they implement\n`Serializable`. Serialisation is based on the **record components** rather\nthan the normal field-based mechanism, which provides better safety\nguarantees:\n\n- No `serialVersionUID` is required (though you can add one).\n- Deserialization always goes through the **canonical constructor**, so\n  validation in the compact constructor is enforced even during\n  deserialisation — unlike regular classes where deserialization bypasses\n  constructors.\n\n```java\nrecord Point(int x, int y) implements Serializable {}\n\n\u002F\u002F Serialise and deserialise:\nbyte[] bytes = serialize(new Point(1, 2));\nPoint p = (Point) deserialize(bytes); \u002F\u002F canonical constructor called\n```\n\n**Rule of thumb:** records are safer to serialise than regular classes\nbecause the canonical constructor (and its validation) is always called on\ndeserialisation.\n",{"id":87,"difficulty":35,"q":88,"a":89},"generic-records","Can records be generic?","Yes. Records support type parameters just like regular classes:\n\n```java\nrecord Pair\u003CA, B>(A first, B second) {}\nrecord Result\u003CT>(T value, String message) {}\n\nPair\u003CString, Integer> p = new Pair\u003C>(\"hello\", 42);\nSystem.out.println(p.first());   \u002F\u002F \"hello\"\nSystem.out.println(p.second());  \u002F\u002F 42\n\nResult\u003CList\u003CString>> r = new Result\u003C>(List.of(\"a\", \"b\"), \"ok\");\n```\n\nType bounds also work: `record Bounded\u003CT extends Comparable\u003CT>>(T value) {}`.\n\n**Rule of thumb:** generic records are a clean replacement for generic\ntuple or wrapper classes — concise and type-safe.\n",{"id":91,"difficulty":43,"q":92,"a":93},"record-static-members","Can records have static fields and methods?","Yes. Records can have **static fields**, **static methods**, and\n**instance methods** (beyond the generated accessors). They cannot have\nadditional non-static (instance) fields.\n\n```java\nrecord Temperature(double celsius) {\n    static final double ABSOLUTE_ZERO = -273.15;\n\n    static Temperature ofFahrenheit(double f) {\n        return new Temperature((f - 32) * 5.0 \u002F 9.0);\n    }\n\n    double toFahrenheit() {\n        return celsius * 9.0 \u002F 5.0 + 32;\n    }\n}\n\nTemperature t = Temperature.ofFahrenheit(98.6);\nSystem.out.println(t.celsius());      \u002F\u002F 37.0\nSystem.out.println(t.toFahrenheit()); \u002F\u002F 98.6\n```\n\n**Rule of thumb:** instance methods are fine in records as long as they\ndon't require mutable state — if you need mutable state, use a class.\n",{"id":95,"difficulty":43,"q":96,"a":97},"records-as-map-keys","Why are records good candidates for Map keys or Set elements?","The compiler-generated `equals()` and `hashCode()` are based on **all\ncomponents**, making records correct `Map` keys and `Set` elements\nimmediately without any manual implementation.\n\n```java\nrecord Point(int x, int y) {}\n\nMap\u003CPoint, String> labels = new HashMap\u003C>();\nlabels.put(new Point(0, 0), \"origin\");\nlabels.put(new Point(1, 0), \"unit-x\");\n\nSystem.out.println(labels.get(new Point(0, 0))); \u002F\u002F \"origin\"\n\u002F\u002F new Point(0,0).equals(new Point(0,0)) is true — hashCode matches\n```\n\nCompare with a regular class where you must manually implement\n`equals()`\u002F`hashCode()` or face silent bugs where equal-looking objects\nhash differently.\n\n**Rule of thumb:** records are safe `HashMap`\u002F`HashSet` keys out of the\nbox; regular classes need a careful `equals()`\u002F`hashCode()` override.\n",{"id":99,"difficulty":35,"q":100,"a":101},"record-local","Can records be declared locally inside a method?","Yes. **Local records** (Java 16+) can be declared inside a method, just\nlike local classes. They are useful as lightweight data holders scoped to\na single method.\n\n```java\nList\u003CString> topEmails(List\u003CUser> users) {\n    record Ranked(User user, int score) {}   \u002F\u002F local record\n\n    return users.stream()\n        .map(u -> new Ranked(u, computeScore(u)))\n        .sorted(Comparator.comparingInt(Ranked::score).reversed())\n        .limit(10)\n        .map(r -> r.user().email())\n        .toList();\n}\n```\n\nLocal records are implicitly `static` (they don't capture enclosing\ninstance state).\n\n**Rule of thumb:** local records are ideal for intermediate pipeline\nvalues that need a name but don't deserve a top-level class.\n",15,null,{"description":32},"Java records interview questions — record syntax, canonical constructor, compact constructor, immutability, records vs POJOs, records vs Lombok, generic records, records with interfaces, and limitations of records.","java\u002Fmodern-java\u002Frecords","2026-06-20","LId2ywaOLWSB9Hn9ystYr978RLpW6H-v_KRdAGuKzGs",{"id":110,"title":111,"body":112,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":116,"navigation":38,"order":33,"path":117,"questions":118,"questionsCount":176,"related":103,"seo":177,"seoDescription":178,"stem":179,"subtopic":111,"topic":19,"topicSlug":21,"updated":107,"__hash__":180},"qa\u002Fjava\u002Fmodern-java\u002Fsealed-classes.md","Sealed Classes",{"type":29,"value":113,"toc":114},[],{"title":32,"searchDepth":33,"depth":33,"links":115},[],{},"\u002Fjava\u002Fmodern-java\u002Fsealed-classes",[119,123,127,131,135,139,143,147,151,156,160,164,168,172],{"id":120,"difficulty":43,"q":121,"a":122},"what-are-sealed-classes","What are sealed classes in Java and what problem do they solve?","**Sealed classes** (Java 17, JEP 409) restrict which classes or interfaces\nmay **directly extend or implement** them using a `permits` clause. They\nsolve the problem of open inheritance hierarchies where any unknown class\ncan subclass your type, preventing exhaustive analysis by the compiler or\nby pattern-matching switches.\n\n```java\npublic sealed interface Shape permits Circle, Rectangle, Triangle {}\n```\n\nNow only `Circle`, `Rectangle`, and `Triangle` can implement `Shape`.\nAny other class attempting `implements Shape` is a **compile error**.\n\n**Rule of thumb:** use sealed classes when you own the full set of\nsubtypes and want the compiler to enforce exhaustive handling.\n",{"id":124,"difficulty":43,"q":125,"a":126},"permits-clause","What is the permits clause and when can it be omitted?","The `permits` clause lists every allowed direct subtype. If all permitted\nsubtypes are in the **same source file** as the sealed class, the `permits`\nclause may be **omitted** and the compiler infers it from the file:\n\n```java\n\u002F\u002F Explicit permits — subtypes may be in separate files:\npublic sealed class Expr permits Num, Add, Mul {}\n\n\u002F\u002F Implicit permits — all in one file:\nsealed class Result\u003CT> {\n    record Ok\u003CT>(T value)    extends Result\u003CT> {}\n    record Err(String msg)   extends Result\u003CVoid> {}\n}\n```\n\nWhen subtypes are in **different packages or modules**, the `permits`\nclause is mandatory and the subtypes must be in the same package (for\nnon-modular code) or accessible module.\n\n**Rule of thumb:** omit `permits` for concise single-file hierarchies;\nuse explicit `permits` when subtypes live in separate files.\n",{"id":128,"difficulty":35,"q":129,"a":130},"permitted-subtype-modifiers","What modifiers must a permitted subtype carry?","Every direct subtype of a sealed class\u002Finterface must be marked with\nexactly one of three modifiers:\n\n| Modifier | Meaning |\n|---|---|\n| `final` | Cannot be extended further — closes the hierarchy |\n| `sealed` | Can be extended, but only by its own `permits` list |\n| `non-sealed` | Opens the hierarchy again — anyone can extend this subtype |\n\n```java\nsealed interface Notification permits Email, Push, Sms {}\n\nfinal class Email implements Notification {}    \u002F\u002F no further subclasses\n\nsealed class Push implements Notification       \u002F\u002F further restricted\n    permits AndroidPush, IosPush {}\nfinal class AndroidPush extends Push {}\nfinal class IosPush     extends Push {}\n\nnon-sealed class Sms implements Notification {} \u002F\u002F open again — anyone can extend\nclass SmsPremium extends Sms {}                 \u002F\u002F allowed\n```\n\n**Rule of thumb:** use `final` for leaf types, `sealed` for multi-level\nhierarchies, and `non-sealed` sparingly when you intentionally want to\nre-open extensibility.\n",{"id":132,"difficulty":35,"q":133,"a":134},"sealed-vs-abstract","What is the difference between a sealed class and an abstract class?","An **abstract class** restricts *instantiation* — you can't call `new`\non it — but it places **no restriction on subclassing**; anyone can extend\nit. A **sealed class** restricts *who can subclass it* — only the `permits`\nlist — but a sealed class can itself be concrete or abstract.\n\n| Aspect | abstract class | sealed class |\n|---|---|---|\n| Prevents instantiation | Yes | Only if also `abstract` |\n| Restricts subclassing | No — anyone can extend | Yes — only `permits` list |\n| Compiler exhaustiveness | No | Yes (with switch pattern matching) |\n\n```java\nabstract sealed class Vehicle permits Car, Truck {}\n\u002F\u002F abstract → can't do new Vehicle()\n\u002F\u002F sealed   → only Car and Truck can extend Vehicle\n```\n\n**Rule of thumb:** combine `abstract sealed` when you want both — no direct\ninstances AND a closed subtype set.\n",{"id":136,"difficulty":43,"q":137,"a":138},"sealed-interfaces","Can interfaces be sealed?","Yes. Sealed interfaces work identically to sealed classes but with\n`implements` instead of `extends`. They are particularly useful with\n**records**, since records are implicitly `final` and naturally close the\nhierarchy:\n\n```java\nsealed interface JsonValue\n    permits JsonString, JsonNumber, JsonBool, JsonNull, JsonArray, JsonObject {}\n\nrecord JsonString(String value) implements JsonValue {}\nrecord JsonNumber(double value) implements JsonValue {}\nrecord JsonBool(boolean value)  implements JsonValue {}\nrecord JsonNull()               implements JsonValue {}\n\u002F\u002F JsonArray and JsonObject might be non-record classes\n```\n\n**Rule of thumb:** sealed interfaces + records are the idiomatic way to\nmodel **algebraic data types (ADTs)** in Java — think Rust enums or\nHaskell sum types.\n",{"id":140,"difficulty":35,"q":141,"a":142},"exhaustive-switch","How do sealed classes enable exhaustive switch expressions?","Because the compiler knows the complete set of subtypes of a sealed type,\na `switch` expression over that type can be verified as **exhaustive**\nat compile time — no default case needed:\n\n```java\nsealed interface Shape permits Circle, Square {}\nrecord Circle(double r) implements Shape {}\nrecord Square(double side) implements Shape {}\n\ndouble area(Shape s) {\n    return switch (s) {              \u002F\u002F exhaustive — compiler knows all cases\n        case Circle c  -> Math.PI * c.r() * c.r();\n        case Square sq -> sq.side() * sq.side();\n    };\n    \u002F\u002F No default needed; adding a new Shape subtype would cause a compile error here\n}\n```\n\nIf you add a new permitted subtype later, every exhaustive switch that\ndoesn't handle it becomes a **compile error** — the compiler guides you\nto all the places that need updating.\n\n**Rule of thumb:** exhaustive switch over a sealed type is the Java\nreplacement for the visitor pattern — compile-time safety instead of\nruntime dispatch tables.\n",{"id":144,"difficulty":35,"q":145,"a":146},"sealed-and-pattern-matching","How do sealed classes combine with pattern matching?","Pattern matching in `switch` (Java 21) gains the most power when used\nwith sealed types because the compiler can check exhaustiveness. You can\nalso **deconstruct record components** inline:\n\n```java\nsealed interface Expr permits Num, Add {}\nrecord Num(int value)       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\nSystem.out.println(eval(new Add(new Num(1), new Num(2)))); \u002F\u002F 3\n```\n\n**Rule of thumb:** sealed + records + switch pattern matching together\nmodel recursive data structures (ASTs, JSON trees, result types) cleanly\nand safely.\n",{"id":148,"difficulty":35,"q":149,"a":150},"sealed-vs-enum","When would you use a sealed class instead of an enum?","**Enums** are fixed singletons — each constant is a single shared instance\nwith no per-instance state beyond what's in enum fields. **Sealed classes**\nallow each subtype to carry **different data** and have distinct structure.\n\n| | Enum | Sealed class |\n|---|---|---|\n| Per-instance data | Same fields for all constants | Each subtype can have different fields |\n| Extensibility | Closed (compile time) | Closed (compile time) |\n| Exhaustive switch | Yes | Yes (Java 21) |\n| Polymorphic behaviour | Via abstract methods | Via subtype methods |\n\n```java\n\u002F\u002F Enum: same structure, different values\nenum Status { PENDING, ACTIVE, CLOSED }\n\n\u002F\u002F Sealed: different structure per variant\nsealed interface Event permits OrderPlaced, OrderShipped, OrderCancelled {}\nrecord OrderPlaced(String orderId, Instant at)         implements Event {}\nrecord OrderShipped(String orderId, String carrier)    implements Event {}\nrecord OrderCancelled(String orderId, String reason)   implements Event {}\n```\n\n**Rule of thumb:** use enum when all variants are structurally identical;\nuse sealed class when variants carry different data.\n",{"id":152,"difficulty":153,"q":154,"a":155},"sealed-with-generics","hard","Can sealed classes be generic?","Yes. Sealed classes and interfaces can be generic, and permitted subtypes\nmay fix or forward the type parameters:\n\n```java\nsealed interface Either\u003CL, R> permits Either.Left, Either.Right {\n    record Left\u003CL, R>(L value)  implements Either\u003CL, R> {}\n    record Right\u003CL, R>(R value) implements Either\u003CL, R> {}\n}\n\nEither\u003CString, Integer> result = new Either.Left\u003C>(\"error\");\n\nString msg = switch (result) {\n    case Either.Left\u003CString, Integer>(String s)  -> \"Error: \" + s;\n    case Either.Right\u003CString, Integer>(Integer n) -> \"Value: \" + n;\n};\n```\n\n**Rule of thumb:** generic sealed types model functional algebraic\ntypes like `Either`, `Option`, or `Result` natively in Java without\nthird-party libraries.\n",{"id":157,"difficulty":153,"q":158,"a":159},"sealed-and-reflection","How can you inspect a sealed class's permitted subtypes at runtime?","Java 17 added `Class.permittedSubclasses()` which returns an array of\n`ClassDesc` descriptors (or use `Class.getPermittedSubclasses()` which\nreturns `Class\u003C?>[]` in preview; in standard API it returns\n`ClassDesc[]`). In practice most code uses the standard method:\n\n```java\nsealed interface Shape permits Circle, Square {}\nfinal record Circle(double r) implements Shape {}\nfinal record Square(double s) implements Shape {}\n\nClass\u003C?>[] permitted = Shape.class.getPermittedSubclasses();\nfor (Class\u003C?> c : permitted) {\n    System.out.println(c.getName());\n    \u002F\u002F prints: Circle, Square\n}\n```\n\nThis allows frameworks (serialisation libraries, UI generators) to\ndiscover the full closed set of subtypes at runtime without classpath\nscanning.\n\n**Rule of thumb:** `getPermittedSubclasses()` gives you the closed type\nset at runtime — use it in frameworks to drive deserialization or\nschema generation without annotation scanning.\n",{"id":161,"difficulty":35,"q":162,"a":163},"sealed-modelling-result","How would you model a Result type (success or failure) using sealed classes?","Sealed classes are the idiomatic way to represent **sum types** like\n`Result\u003CT>` — a value that is either a success carrying a result or a\nfailure carrying an error:\n\n```java\nsealed interface Result\u003CT> permits Result.Ok, Result.Err {\n    record Ok\u003CT>(T value)      implements Result\u003CT> {}\n    record Err\u003CT>(String msg)  implements Result\u003CT> {}\n}\n\nResult\u003CInteger> parse(String s) {\n    try { return new Result.Ok\u003C>(Integer.parseInt(s)); }\n    catch (NumberFormatException e) { return new Result.Err\u003C>(e.getMessage()); }\n}\n\n\u002F\u002F Pattern-match at call site:\nswitch (parse(\"42\")) {\n    case Result.Ok\u003CInteger>(int v)  -> System.out.println(\"Parsed: \" + v);\n    case Result.Err\u003CInteger>(String m) -> System.err.println(\"Failed: \" + m);\n}\n```\n\n**Rule of thumb:** a sealed `Result` type makes error paths visible in\nthe type system — callers *must* handle both cases, unlike checked\nexceptions which are often swallowed.\n",{"id":165,"difficulty":35,"q":166,"a":167},"non-sealed-purpose","Why would you mark a permitted subtype as non-sealed?","`non-sealed` intentionally **re-opens** the hierarchy below a sealed\ntype. You'd use it when you own the sealed root (for exhaustive internal\ndispatch) but want to allow third-party code to extend a specific branch:\n\n```java\nsealed interface Plugin permits CorePlugin, ExtensionPlugin {}\nfinal class CorePlugin    implements Plugin {}   \u002F\u002F internal, closed\nnon-sealed class ExtensionPlugin implements Plugin {} \u002F\u002F open to third parties\n\nclass MyCustomPlugin extends ExtensionPlugin {}  \u002F\u002F allowed\n```\n\nThe sealed root still gives you a closed set for internal `switch`\nexpressions that only need to distinguish `CorePlugin` vs\n`ExtensionPlugin` (not their subtypes).\n\n**Rule of thumb:** use `non-sealed` to create a deliberate extension\npoint in an otherwise closed hierarchy — document it clearly as a\npublic API contract.\n",{"id":169,"difficulty":43,"q":170,"a":171},"sealed-compile-error-example","What compile errors do sealed classes prevent?","Any class not listed in the `permits` clause that tries to extend or\nimplement the sealed type gets a **compile error**:\n\n```java\nsealed interface Animal permits Dog, Cat {}\nfinal class Dog implements Animal {}\nfinal class Cat implements Animal {}\n\n\u002F\u002F This causes a compile error:\n\u002F\u002F class Fish implements Animal {}\n\u002F\u002F error: Fish is not allowed in the sealed hierarchy\n\n\u002F\u002F This also fails — permitted subtypes must have final\u002Fsealed\u002Fnon-sealed:\n\u002F\u002F class Dog implements Animal {}   \u002F\u002F error: missing modifier\n```\n\nThe compiler also **warns on exhaustive switch** if a new subtype is\nadded to the `permits` list but not handled in existing switches.\n\n**Rule of thumb:** sealed classes turn runtime `ClassCastException` or\nmissed-case bugs into **compile errors** — fix them at build time, not\nin production.\n",{"id":173,"difficulty":35,"q":174,"a":175},"sealed-jdk-examples","Does the JDK itself use sealed classes?","Yes. Several JDK APIs introduced in Java 17–21 use sealed types:\n\n- **`java.lang.constant.ConstantDesc`** — sealed interface with\n  `ClassDesc`, `MethodTypeDesc`, `DynamicConstantDesc`, etc.\n- **`jdk.incubator.vector`** — sealed `VectorShape` hierarchy.\n- Pattern matching in the `switch` expression itself — the language\n  spec describes case selectors using a sealed hierarchy of pattern kinds.\n\n```java\n\u002F\u002F ConstantDesc is sealed — you can exhaustively switch over it:\nConstantDesc desc = ...;\nswitch (desc) {\n    case ClassDesc cd       -> ...;\n    case MethodTypeDesc md  -> ...;\n    \u002F\u002F etc.\n}\n```\n\n**Rule of thumb:** look at `java.lang.constant` for a production-quality\nexample of sealed interfaces in the JDK itself.\n",14,{"description":32},"Java sealed classes interview questions — permits clause, sealed vs abstract, permitted subtypes (final\u002Fsealed\u002Fnon-sealed), sealed interfaces, exhaustive switch, algebraic data types, and sealed classes with records.","java\u002Fmodern-java\u002Fsealed-classes","9QpfaTliEY2U89RE3zmAYqNt6DxbvRpm8_UelBaYDP4",{"id":182,"title":183,"body":184,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":188,"navigation":38,"order":189,"path":190,"questions":191,"questionsCount":176,"related":103,"seo":248,"seoDescription":249,"stem":250,"subtopic":183,"topic":19,"topicSlug":21,"updated":107,"__hash__":251},"qa\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching.md","Switch Pattern Matching",{"type":29,"value":185,"toc":186},[],{"title":32,"searchDepth":33,"depth":33,"links":187},[],{},3,"\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching",[192,196,200,204,208,212,216,220,224,228,232,236,240,244],{"id":193,"difficulty":43,"q":194,"a":195},"switch-expression-vs-statement","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":197,"difficulty":43,"q":198,"a":199},"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":201,"difficulty":43,"q":202,"a":203},"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":205,"difficulty":35,"q":206,"a":207},"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":209,"difficulty":35,"q":210,"a":211},"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":213,"difficulty":35,"q":214,"a":215},"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":217,"difficulty":35,"q":218,"a":219},"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":221,"difficulty":35,"q":222,"a":223},"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":225,"difficulty":43,"q":226,"a":227},"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":229,"difficulty":153,"q":230,"a":231},"dominance-ordering","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":233,"difficulty":43,"q":234,"a":235},"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":237,"difficulty":43,"q":238,"a":239},"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":241,"difficulty":43,"q":242,"a":243},"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":245,"difficulty":35,"q":246,"a":247},"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",{"description":32},"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","GAwpTTmi-7w5m78Th5Ygzha-X_UEF8pxOyLiB6lfN2w",{"id":253,"title":254,"body":255,"description":32,"difficulty":43,"extension":36,"framework":10,"frameworkSlug":8,"meta":259,"navigation":38,"order":260,"path":261,"questions":262,"questionsCount":311,"related":103,"seo":312,"seoDescription":313,"stem":314,"subtopic":254,"topic":19,"topicSlug":21,"updated":107,"__hash__":315},"qa\u002Fjava\u002Fmodern-java\u002Ftext-blocks.md","Text Blocks",{"type":29,"value":256,"toc":257},[],{"title":32,"searchDepth":33,"depth":33,"links":258},[],{},4,"\u002Fjava\u002Fmodern-java\u002Ftext-blocks",[263,267,271,275,279,283,287,291,295,299,303,307],{"id":264,"difficulty":43,"q":265,"a":266},"what-are-text-blocks","What are text blocks in Java and what problem do they solve?","**Text blocks** (Java 15, JEP 378) are multi-line string literals enclosed\nin triple double-quotes (`\"\"\"`). They eliminate the need for explicit `\\n`,\n`\\\"`, and string concatenation when embedding multi-line content like JSON,\nSQL, HTML, or XML.\n\n```java\n\u002F\u002F Old way — hard to read, easy to make mistakes:\nString json = \"{\\n\" +\n              \"  \\\"name\\\": \\\"Alice\\\",\\n\" +\n              \"  \\\"age\\\": 30\\n\" +\n              \"}\";\n\n\u002F\u002F Text block — reads like the actual content:\nString json = \"\"\"\n        {\n          \"name\": \"Alice\",\n          \"age\": 30\n        }\n        \"\"\";\n```\n\n**Rule of thumb:** use text blocks for any multi-line string — SQL,\nJSON, HTML, XML, or test fixtures; never concatenate `\\n` manually again.\n",{"id":268,"difficulty":43,"q":269,"a":270},"opening-closing-delimiter","What are the rules for the opening and closing \"\"\" delimiters?","- The **opening** `\"\"\"` must be followed by a newline — content cannot\n  start on the same line as the opening delimiter.\n- The **closing** `\"\"\"` can be on its own line or at the end of the last\n  content line.\n- The position of the closing `\"\"\"` determines the amount of\n  **incidental whitespace** stripped.\n\n```java\n\u002F\u002F Closing \"\"\" on its own line — trailing newline included:\nString s1 = \"\"\"\n        hello\n        world\n        \"\"\";\n\u002F\u002F s1 = \"hello\\nworld\\n\"\n\n\u002F\u002F Closing \"\"\" at end of last line — no trailing newline:\nString s2 = \"\"\"\n        hello\n        world\"\"\";\n\u002F\u002F s2 = \"hello\\nworld\"\n```\n\n**Rule of thumb:** put the closing `\"\"\"` on its own line for content that\nshould end with a newline (files, SQL); inline it to suppress the trailing\nnewline.\n",{"id":272,"difficulty":35,"q":273,"a":274},"incidental-whitespace","What is incidental whitespace and how does the compiler strip it?","**Incidental whitespace** is the leading spaces added to align a text\nblock with the surrounding code indentation. The compiler removes it\nautomatically, keeping only the relative indentation within the block.\n\nThe algorithm finds the **common leading whitespace prefix** across all\nnon-blank content lines and the closing delimiter line, then strips that\nprefix from every line:\n\n```java\nString sql = \"\"\"\n        SELECT *\n        FROM users\n        WHERE active = true\n        \"\"\";\n\u002F\u002F Each content line has 12 spaces; closing \"\"\" has 12 spaces.\n\u002F\u002F Compiler strips 12 spaces → result:\n\u002F\u002F \"SELECT *\\nFROM users\\nWHERE active = true\\n\"\n```\n\nYou can **increase** relative indentation by indenting content lines more\nthan the closing delimiter:\n\n```java\nString indented = \"\"\"\n        outer\n            inner\n        \"\"\";\n\u002F\u002F \"outer\\n    inner\\n\"  — inner has 4 extra spaces relative to outer\n```\n\n**Rule of thumb:** align the closing `\"\"\"` with the minimum indentation\nyou want in the output; the compiler strips everything up to that column.\n",{"id":276,"difficulty":35,"q":277,"a":278},"line-endings","How does Java handle line endings in text blocks?","The Java compiler **normalises** all line endings in text blocks to `\\n`\n(LF) regardless of the OS line ending in the source file. This makes\ntext blocks portable across Windows (CRLF) and Unix (LF) systems without\nany special handling.\n\n```java\nString s = \"\"\"\n        line1\n        line2\n        \"\"\";\n\u002F\u002F Always \"line1\\nline2\\n\" — never \"line1\\r\\nline2\\r\\n\"\n```\n\nIf you explicitly need `\\r\\n` (e.g., for HTTP headers or email), use\nthe `\\r` escape:\n\n```java\nString crlf = \"\"\"\n        Content-Type: text\u002Fhtml\\r\n        Content-Length: 42\\r\n        \\r\n        \"\"\";\n```\n\n**Rule of thumb:** text blocks use `\\n` everywhere — don't worry about\nCRLF portability; handle it explicitly only when the protocol requires it.\n",{"id":280,"difficulty":35,"q":281,"a":282},"text-block-escapes","What new escape sequences were introduced for text blocks?","Java 15 added two new escape sequences specifically for text blocks:\n\n**`\\\u003Cline terminator>`** (line continuation) — suppresses the newline at\nthe end of a line, joining it with the next line. Useful for long logical\nlines you want to wrap visually:\n\n```java\nString sentence = \"\"\"\n        The quick brown fox \\\n        jumps over the lazy dog.\n        \"\"\";\n\u002F\u002F \"The quick brown fox jumps over the lazy dog.\\n\"\n```\n\n**`\\s`** (explicit space) — a single space that prevents trailing-space\nstripping. The compiler strips trailing whitespace from each line; `\\s`\nmarks a space that must be preserved:\n\n```java\nString padded = \"\"\"\n        red  \\s\n        green\\s\n        blue \\s\n        \"\"\";\n\u002F\u002F Each line ends with exactly one trailing space\n```\n\n**Rule of thumb:** use `\\` to join wrapped lines; use `\\s` to preserve\ntrailing spaces that would otherwise be stripped.\n",{"id":284,"difficulty":43,"q":285,"a":286},"formatted-method","How do you interpolate values into a text block?","Text blocks do not support template syntax natively. Use\n`String.formatted()` (or `String.format()`) to substitute values:\n\n```java\nString name = \"Alice\";\nint age = 30;\n\nString json = \"\"\"\n        {\n          \"name\": \"%s\",\n          \"age\": %d\n        }\n        \"\"\".formatted(name, age);\n\u002F\u002F {\"name\":\"Alice\",\"age\":30}\n```\n\n`String.formatted()` (added in Java 15) is the instance-method equivalent\nof `String.format()`, making the chain read naturally after a text block.\n\n**Rule of thumb:** use `.formatted()` for simple substitution; for\ncomplex templates with loops or conditionals, prefer a template engine\n(Mustache, Thymeleaf) rather than string manipulation.\n",{"id":288,"difficulty":43,"q":289,"a":290},"text-block-vs-string","Is a text block a different type from String?","No. A text block is just a **compile-time literal** that produces a\nregular `java.lang.String`. At runtime there is no difference between a\ntext block and a regular `String` literal — they are the same type.\n\n```java\nString a = \"hello\\nworld\\n\";\nString b = \"\"\"\n        hello\n        world\n        \"\"\";\nSystem.out.println(a.equals(b)); \u002F\u002F true — same content, same type\nSystem.out.println(a == b);      \u002F\u002F likely true — interned constants\n```\n\nBecause text blocks are string literals they are **interned** (pooled)\njust like regular literals.\n\n**Rule of thumb:** treat text blocks as a nicer syntax for string\nliterals — same type, same pool, same API.\n",{"id":292,"difficulty":43,"q":293,"a":294},"text-block-sql","Show a realistic example of a text block used for SQL.","Text blocks make multi-line SQL readable and maintainable, preserving\nthe visual structure of the query:\n\n```java\nString query = \"\"\"\n        SELECT u.id, u.name, o.total\n        FROM users u\n        JOIN orders o ON o.user_id = u.id\n        WHERE u.active = true\n          AND o.created_at > :since\n        ORDER BY o.total DESC\n        LIMIT :limit\n        \"\"\";\n\n\u002F\u002F Use with JDBC or JPA:\nQuery q = em.createNativeQuery(query, UserOrderDto.class)\n            .setParameter(\"since\", startDate)\n            .setParameter(\"limit\", 100);\n```\n\nCompare this to the pre-Java-15 alternative of quote-and-concatenate —\nthe text block is directly readable and diffable.\n\n**Rule of thumb:** text blocks are especially valuable for SQL, where\nkeyword alignment (SELECT\u002FFROM\u002FWHERE\u002FORDER BY) aids readability at a glance.\n",{"id":296,"difficulty":35,"q":297,"a":298},"text-block-trailing-whitespace","What happens to trailing whitespace on lines inside a text block?","The compiler **strips trailing whitespace** from every line of a text\nblock. This means spaces or tabs at the end of a content line are silently\nremoved. This is usually desirable (cleaner output), but can surprise you\nif you intend to produce trailing spaces (e.g., in fixed-width formatted\noutput).\n\n```java\nString padded = \"\"\"\n        item     \n        total    \n        \"\"\";\n\u002F\u002F Trailing spaces on each line are GONE:\n\u002F\u002F \"item\\ntotal\\n\"\n```\n\nUse `\\s` to anchor a trailing space that must be preserved:\n\n```java\nString padded = \"\"\"\n        item  \\s\n        total \\s\n        \"\"\";\n\u002F\u002F \"item  \\ntotal \\n\"  — one preserved trailing space per line\n```\n\n**Rule of thumb:** never rely on trailing spaces in text blocks; use\n`\\s` explicitly whenever a trailing space is meaningful.\n",{"id":300,"difficulty":43,"q":301,"a":302},"text-block-html","Show a realistic example of a text block used for HTML.","Text blocks eliminate the visual noise of escaping quotes in HTML\nattributes:\n\n```java\nString html = \"\"\"\n        \u003C!DOCTYPE html>\n        \u003Chtml lang=\"en\">\n        \u003Chead>\n          \u003Cmeta charset=\"UTF-8\">\n          \u003Ctitle>%s\u003C\u002Ftitle>\n        \u003C\u002Fhead>\n        \u003Cbody>\n          \u003Ch1>%s\u003C\u002Fh1>\n        \u003C\u002Fbody>\n        \u003C\u002Fhtml>\n        \"\"\".formatted(title, heading);\n```\n\nPreviously every `\"` inside the HTML needed escaping as `\\\"`, making\nattributes like `lang=\"en\"` look like `lang=\\\"en\\\"` — difficult to read\nand error-prone to edit.\n\n**Rule of thumb:** if your string contains `\"` characters (HTML, JSON,\nSQL string literals), a text block eliminates escape clutter entirely.\n",{"id":304,"difficulty":35,"q":305,"a":306},"text-block-indentation-control","How do you control indentation in the output of a text block programmatically?","Java 15 also added `String.indent(int n)` which adjusts the indentation\nof each line in a string by `n` spaces (positive = add, negative = remove):\n\n```java\nString block = \"\"\"\n        line1\n        line2\n        \"\"\";\n\n\u002F\u002F Add 4 spaces of indentation to every line:\nSystem.out.print(block.indent(4));\n\u002F\u002F     line1\n\u002F\u002F     line2\n\u002F\u002F                ← trailing newline preserved\n\n\u002F\u002F Remove spaces (clamped at 0 per line):\nSystem.out.print(block.indent(-2));\n```\n\n`String.stripIndent()` (also Java 15) performs the same incidental\nwhitespace removal the compiler does, useful for text blocks received\nfrom external sources at runtime.\n\n**Rule of thumb:** use `indent()` for post-processing indentation;\nuse `stripIndent()` to clean up dynamically loaded multi-line strings\nthe same way the compiler cleans text block literals.\n",{"id":308,"difficulty":43,"q":309,"a":310},"text-block-java-version","In which Java version did text blocks become a standard feature?","Text blocks were introduced as a **preview** feature in Java 13 (JEP 355)\nand Java 14 (JEP 368), then finalized as a standard feature in\n**Java 15** (JEP 378). No `--enable-preview` flag is needed in Java 15+.\n\n```java\n\u002F\u002F Java 13\u002F14 — required --enable-preview at compile and runtime\n\u002F\u002F Java 15+   — standard feature, no flag needed\n\nString query = \"\"\"\n        SELECT *\n        FROM orders\n        \"\"\";\n```\n\n**Rule of thumb:** text blocks are available unconditionally in Java 15+;\nif your codebase targets Java 15 or higher you should use them freely\nfor all multi-line string content.\n",12,{"description":32},"Java text blocks interview questions — triple-quote syntax, incidental whitespace stripping, line endings, escape sequences, formatted() method, and comparing text blocks to string concatenation and heredocs.","java\u002Fmodern-java\u002Ftext-blocks","01Xl11eivLS_NDw9BhLS7TxxvE-5YEhzNT9TbxQxKXw",{"id":317,"title":318,"body":319,"description":32,"difficulty":43,"extension":36,"framework":10,"frameworkSlug":8,"meta":323,"navigation":38,"order":11,"path":324,"questions":325,"questionsCount":311,"related":103,"seo":374,"seoDescription":375,"stem":376,"subtopic":377,"topic":19,"topicSlug":21,"updated":107,"__hash__":378},"qa\u002Fjava\u002Fmodern-java\u002Finstanceof-pattern-matching.md","Instanceof Pattern Matching",{"type":29,"value":320,"toc":321},[],{"title":32,"searchDepth":33,"depth":33,"links":322},[],{},"\u002Fjava\u002Fmodern-java\u002Finstanceof-pattern-matching",[326,330,334,338,342,346,350,354,358,362,366,370],{"id":327,"difficulty":43,"q":328,"a":329},"what-is-instanceof-pattern-matching","What is pattern matching for instanceof and what problem does it solve?","**Pattern matching for `instanceof`** (Java 16, JEP 394) combines a type\ntest with a **binding variable** in a single expression, eliminating the\nredundant cast that always followed a traditional `instanceof` check.\n\n```java\n\u002F\u002F Old way — test then cast:\nif (obj instanceof String) {\n    String s = (String) obj;   \u002F\u002F redundant — we already know it's a String\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 bound here, no cast needed\n}\n```\n\n**Rule of thumb:** if you ever follow `instanceof` with a cast to the same\ntype on the next line, rewrite it as a pattern matching `instanceof`.\n",{"id":331,"difficulty":35,"q":332,"a":333},"binding-variable-scope","What is the scope of the binding variable in instanceof pattern matching?","The binding variable is in scope only where the compiler can prove the\npattern has matched — this is called **definite assignment** based on\n**flow-sensitive typing**:\n\n```java\nObject obj = getObject();\n\nif (obj instanceof String s) {\n    System.out.println(s.length()); \u002F\u002F s in scope: pattern matched\n}\n\u002F\u002F s NOT in scope here\n\n\u002F\u002F Also works in the same condition with &&:\nif (obj instanceof String s && s.length() > 5) {\n    System.out.println(s.toUpperCase()); \u002F\u002F s in scope in both parts of &&\n}\n\n\u002F\u002F NOT in scope with ||:\n\u002F\u002F if (obj instanceof String s || s.isEmpty()) { } \u002F\u002F compile error\n```\n\n**Rule of thumb:** the binding variable is in scope in the `true` branch\nand in `&&` chains; it is NOT in scope in the `false` branch or `||` chains.\n",{"id":335,"difficulty":35,"q":336,"a":337},"negation-pattern","How does instanceof pattern matching work with negation?","When you negate the pattern with `!`, the binding variable is in scope\nin the **false \u002F else branch** because that's where the match is guaranteed\nnot to hold — so the variable would be undefined there. Instead, the\nvariable is in scope after an early `return` or `throw`:\n\n```java\nvoid process(Object obj) {\n    if (!(obj instanceof String s)) {\n        throw new IllegalArgumentException(\"Expected String\");\n    }\n    \u002F\u002F s is in scope here — we know obj is a String\n    System.out.println(s.toUpperCase());\n}\n```\n\nThis pattern is useful for **guard clauses** (early return \u002F fail-fast)\nthat validate types at method entry.\n\n**Rule of thumb:** negate `instanceof` patterns for guard clauses; the\nbinding variable flows into scope after the guard exits.\n",{"id":339,"difficulty":35,"q":340,"a":341},"compound-conditions","Can you combine instanceof pattern matching with other conditions?","Yes. Pattern variables work naturally with `&&` to add conditions on the\nmatched value:\n\n```java\nObject obj = getObject();\n\n\u002F\u002F Type test + length check in one expression:\nif (obj instanceof String s && s.length() > 10) {\n    System.out.println(\"long string: \" + s);\n}\n\n\u002F\u002F Type test + nullity (null never matches instanceof):\nif (obj instanceof String s) {\n    \u002F\u002F s is guaranteed non-null here — instanceof returns false for null\n}\n\n\u002F\u002F Multiple type checks in sequence:\nif (obj instanceof List\u003C?> list && !list.isEmpty()\n        && list.get(0) instanceof Integer first) {\n    System.out.println(\"First int: \" + first);\n}\n```\n\n**Rule of thumb:** chain `&&` conditions freely after an `instanceof`\npattern — the binding variable is available throughout the `&&` chain.\n",{"id":343,"difficulty":43,"q":344,"a":345},"instanceof-null-behaviour","What does instanceof return for null?","**`instanceof` always returns `false` for `null`**, regardless of the\ntype being tested. This means the binding variable in a pattern matching\n`instanceof` is always non-null when the pattern matches — no explicit\nnull check needed.\n\n```java\nObject obj = null;\nSystem.out.println(obj instanceof String);   \u002F\u002F false\nSystem.out.println(obj instanceof String s); \u002F\u002F false — s never bound\n\n\u002F\u002F Traditional null check is unnecessary:\nif (obj instanceof String s) {\n    \u002F\u002F s is guaranteed non-null here\n    s.length(); \u002F\u002F safe — no NPE\n}\n```\n\n**Rule of thumb:** `instanceof` patterns implicitly handle null safely —\nthe branch is not entered for null values, so no explicit null guard is\nneeded before the pattern test.\n",{"id":347,"difficulty":35,"q":348,"a":349},"pattern-variable-shadowing","Can a pattern variable shadow an existing variable?","A pattern variable can **shadow** a field or outer-scope variable but\n**cannot shadow a local variable** in the same scope:\n\n```java\nclass Processor {\n    String value = \"field\";\n\n    void process(Object obj) {\n        \u002F\u002F Shadows the field 'value' — allowed:\n        if (obj instanceof String value) {\n            System.out.println(value); \u002F\u002F refers to the pattern variable\n        }\n\n        String local = \"existing\";\n        \u002F\u002F Cannot shadow a local in the same scope — compile error:\n        \u002F\u002F if (obj instanceof String local) { }\n    }\n}\n```\n\n**Rule of thumb:** treat pattern variables like regular local variables\nfor scoping — they can shadow fields but not other locals in the same block.\n",{"id":351,"difficulty":43,"q":352,"a":353},"instanceof-vs-cast","Why is the old instanceof + cast pattern considered bad practice now?","The old idiom is **redundant** — you check the type with `instanceof`,\nthen immediately cast to the same type, which the compiler already knows\nis safe:\n\n```java\n\u002F\u002F Anti-pattern — states the type twice:\nif (obj instanceof String) {\n    String s = (String) obj;  \u002F\u002F the cast cannot fail — why write it?\n    ...\n}\n\n\u002F\u002F Correct — DRY, type stated once:\nif (obj instanceof String s) {\n    ...\n}\n```\n\nBeyond redundancy, the old form is slightly error-prone — a typo could\ncast to a different type than was tested, producing a `ClassCastException`\nat runtime instead of a compile error.\n\n**Rule of thumb:** the old `instanceof` + cast is a code smell in Java 16+;\nreplace it with a pattern matching `instanceof` in any code review.\n",{"id":355,"difficulty":35,"q":356,"a":357},"pattern-in-ternary","Can you use instanceof pattern matching in a ternary expression?","You can use the bound variable in the **true branch** of the ternary\nbecause the variable is only in scope where the pattern is guaranteed\nto have matched:\n\n```java\nObject obj = getValue();\n\n\u002F\u002F Binding variable in scope in the true branch:\nString result = obj instanceof String s ? s.toUpperCase() : \"not a string\";\n```\n\nThe variable `s` is not accessible in the false branch, which is correct —\nif `obj` is not a `String`, `s` would be undefined.\n\n**Rule of thumb:** pattern variables in ternaries behave exactly like\nin `if` branches — scoped to the branch where the test is true.\n",{"id":359,"difficulty":35,"q":360,"a":361},"equals-override-example","How does instanceof pattern matching improve the equals() override?","The classic `equals()` override required an `instanceof` check followed\nby a cast. Pattern matching eliminates the cast:\n\n```java\n\u002F\u002F Old way:\n@Override\npublic boolean equals(Object o) {\n    if (!(o instanceof Point)) return false;\n    Point other = (Point) o;          \u002F\u002F redundant cast\n    return x == other.x && y == other.y;\n}\n\n\u002F\u002F With pattern matching (Java 16+):\n@Override\npublic boolean equals(Object o) {\n    return o instanceof Point other   \u002F\u002F test + bind in one step\n        && x == other.x\n        && y == other.y;\n}\n```\n\nThis is more concise and eliminates the possibility of casting to the\nwrong type by mistake.\n\n**Rule of thumb:** pattern matching `instanceof` is the idiomatic way\nto write `equals()` in Java 16+ — test and bind in a single expression.\n",{"id":363,"difficulty":35,"q":364,"a":365},"pattern-matching-relationship-switch","How does instanceof pattern matching relate to switch pattern matching?","They are part of the same **pattern matching** feature family:\n\n- **`instanceof` pattern** (Java 16) — single-type check, one binding variable.\n- **Switch type pattern** (Java 21) — multi-way dispatch, one binding per case.\n\n`instanceof` is the right tool for one or two type checks; switch is better\nfor three or more:\n\n```java\n\u002F\u002F Two types — instanceof is fine:\nif (shape instanceof Circle c) {\n    return Math.PI * c.radius() * c.radius();\n} else if (shape instanceof Rectangle r) {\n    return r.w() * r.h();\n}\n\n\u002F\u002F Three or more types — switch is cleaner and exhaustive:\ndouble area = switch (shape) {\n    case Circle c        -> Math.PI * c.radius() * c.radius();\n    case Rectangle r     -> r.w() * r.h();\n    case Triangle t      -> 0.5 * t.base() * t.height();\n};\n```\n\n**Rule of thumb:** use `instanceof` patterns for one or two cases;\nswitch patterns for multi-way dispatch — especially when exhaustiveness\nchecking with sealed types is valuable.\n",{"id":367,"difficulty":43,"q":368,"a":369},"no-reassignment","Can you reassign a pattern binding variable?","Pattern binding variables are **effectively final by default** but the\ncompiler does not actually enforce `final` — you can reassign them within\nthe scope. However, reassigning a pattern variable is considered bad\npractice because it breaks the clarity of the pattern-matched binding:\n\n```java\nif (obj instanceof String s) {\n    s = s.trim(); \u002F\u002F legal — but confusing: s no longer equals obj cast\n    System.out.println(s);\n}\n\n\u002F\u002F Better — use a new variable:\nif (obj instanceof String s) {\n    String trimmed = s.trim();\n    System.out.println(trimmed);\n}\n```\n\n**Rule of thumb:** treat pattern binding variables as read-only; assign\nto a new variable if you need a derived value.\n",{"id":371,"difficulty":153,"q":372,"a":373},"generic-types-pattern-matching","How does instanceof pattern matching work with generic types?","Due to **type erasure**, you can only test against the **raw type**,\nnot a parameterised generic type. `obj instanceof List\u003CString>` is a\ncompile error because the generic argument is erased at runtime:\n\n```java\nObject obj = List.of(\"a\", \"b\");\n\n\u002F\u002F Compile error — generic argument not checkable at runtime:\n\u002F\u002F if (obj instanceof List\u003CString> strings) { }\n\n\u002F\u002F Use wildcard or raw type:\nif (obj instanceof List\u003C?> list) {\n    \u002F\u002F list is List\u003C?> — elements are typed as Object\n    list.forEach(System.out::println);\n}\n```\n\nIf you need to verify element types, you must check each element\nindividually inside the branch.\n\n**Rule of thumb:** test against `List\u003C?>` or the raw type with\n`instanceof`; type erasure prevents checking the generic argument.\n",{"description":32},"Java instanceof pattern matching interview questions — binding variables, scope rules, negation patterns, compound conditions, type patterns in switch, comparing old instanceof to new syntax, and practical use cases.","java\u002Fmodern-java\u002Finstanceof-pattern-matching","instanceof Pattern Matching","4HVBgAnGGLtkslxAGvvF-Xb7Oy6BMZR3ev-jCs2CDFo",{"id":380,"title":381,"body":382,"description":32,"difficulty":153,"extension":36,"framework":10,"frameworkSlug":8,"meta":386,"navigation":38,"order":387,"path":388,"questions":389,"questionsCount":442,"related":103,"seo":443,"seoDescription":444,"stem":445,"subtopic":381,"topic":19,"topicSlug":21,"updated":107,"__hash__":446},"qa\u002Fjava\u002Fmodern-java\u002Fvirtual-threads.md","Virtual Threads",{"type":29,"value":383,"toc":384},[],{"title":32,"searchDepth":33,"depth":33,"links":385},[],{},6,"\u002Fjava\u002Fmodern-java\u002Fvirtual-threads",[390,394,398,402,406,410,414,418,422,426,430,434,438],{"id":391,"difficulty":43,"q":392,"a":393},"what-are-virtual-threads","What are virtual threads in Java and what problem do they solve?","**Virtual threads** (Java 21, JEP 444, Project Loom) are lightweight\nthreads managed by the JVM rather than the OS. A traditional\n**platform thread** maps 1-to-1 to an OS thread — costly (~1 MB stack,\nslow to create, limited in number). A virtual thread is a JVM-managed\nconstruct that is **mounted onto a carrier platform thread** only when it\nneeds CPU time, and **unmounted** (parked) during blocking I\u002FO.\n\n```java\n\u002F\u002F Platform thread — one OS thread, ~1 MB stack:\nThread t = new Thread(task);\n\n\u002F\u002F Virtual thread — lightweight, ~few KB, millions possible:\nThread vt = Thread.ofVirtual().start(task);\n```\n\nThis enables a **thread-per-request** model where each HTTP request gets\nits own thread, but the JVM handles thousands of concurrent requests with\nonly a handful of OS threads underneath.\n\n**Rule of thumb:** virtual threads are not faster threads — they are\n*cheap* threads that eliminate the need to avoid blocking I\u002FO.\n",{"id":395,"difficulty":35,"q":396,"a":397},"platform-vs-virtual-threads","How do virtual threads differ from platform threads?","| Aspect | Platform Thread | Virtual Thread |\n|---|---|---|\n| OS mapping | 1:1 with OS thread | Many-to-few (M:N) |\n| Stack size | ~1 MB (fixed) | ~few KB (growable) |\n| Creation cost | High (~ms) | Very low (~µs) |\n| Max concurrent | ~thousands | ~millions |\n| Blocking I\u002FO | Blocks the OS thread | JVM unmounts; carrier thread reused |\n| Thread pool needed | Yes, to limit overhead | Generally no |\n| `synchronized` | Full support | Risk of pinning (see pinning) |\n\n```java\n\u002F\u002F Platform threads need a pool to limit resource usage:\nExecutorService pool = Executors.newFixedThreadPool(200);\n\n\u002F\u002F Virtual threads: create freely per task\nExecutorService vExecutor = Executors.newVirtualThreadPerTaskExecutor();\n```\n\n**Rule of thumb:** virtual threads are the right default for I\u002FO-bound\nconcurrent tasks; platform threads remain necessary for CPU-bound work.\n",{"id":399,"difficulty":35,"q":400,"a":401},"carrier-threads","What is a carrier thread and how does the JVM schedule virtual threads?","A **carrier thread** is a platform thread that a virtual thread runs on.\nThe JVM maintains a small **ForkJoinPool** of carrier threads (defaulting\nto the number of CPU cores). Virtual threads are scheduled by the JVM onto\navailable carrier threads using **cooperative scheduling**:\n\n1. Virtual thread starts executing on a carrier thread.\n2. When it hits a blocking operation (I\u002FO, `sleep`, `LockSupport.park`),\n   the JVM **unmounts** the virtual thread from the carrier — saving its\n   stack to the heap.\n3. The carrier thread is now free to run another virtual thread.\n4. When the I\u002FO completes, the virtual thread is **remounted** on any\n   available carrier thread and resumes.\n\n```\nOS threads (carrier pool): [T1] [T2] [T3] [T4]\nVirtual threads:           [V1] [V2] ... [V10000]\nV1 blocks on I\u002FO → unmounted from T1 → T1 picks up V2 immediately\n```\n\n**Rule of thumb:** the carrier pool size is set by `-Djdk.virtualThreadScheduler.parallelism=N`\n(default = CPU cores); don't increase it for I\u002FO-bound workloads.\n",{"id":403,"difficulty":35,"q":404,"a":405},"thread-per-request-model","How do virtual threads enable the thread-per-request model?","Before virtual threads, thread-per-request was impractical beyond a few\nhundred concurrent requests because each request consumed an OS thread.\nFrameworks like Servlet, Spring MVC, and JDBC used **thread pools** and\n**reactive\u002Fasync** APIs to avoid blocking.\n\nWith virtual threads, the JVM handles the parking\u002Fscheduling:\n\n```java\n\u002F\u002F Spring Boot 3.2+ — enable virtual threads globally:\n\u002F\u002F spring.threads.virtual.enabled=true\n\n\u002F\u002F Or manually with Jakarta Servlets:\ntry (var executor = Executors.newVirtualThreadPerTaskExecutor()) {\n    executor.submit(() -> {\n        String data = jdbcTemplate.queryForObject(sql, String.class); \u002F\u002F blocks OK\n        return processData(data);\n    });\n}\n```\n\nEach request can use **blocking APIs** (JDBC, REST client, file I\u002FO)\nnaturally — the JVM suspends the virtual thread during the wait without\nconsuming an OS thread.\n\n**Rule of thumb:** virtual threads make simple synchronous blocking code\nscale as well as complex asynchronous reactive code — without callback\nhell or `CompletableFuture` chains.\n",{"id":407,"difficulty":153,"q":408,"a":409},"pinning","What is thread pinning in virtual threads and how do you avoid it?","**Pinning** occurs when a virtual thread cannot be unmounted from its\ncarrier thread during a blocking operation. This defeats the purpose of\nvirtual threads because the carrier OS thread is held blocked.\n\nTwo situations cause pinning:\n1. **`synchronized` blocks or methods** — the JVM cannot currently\n   unmount a virtual thread that holds a monitor lock.\n2. **Native methods** — JNI frames cannot be parked.\n\n```java\nsynchronized (lock) {\n    Thread.sleep(1000); \u002F\u002F virtual thread PINNED — carrier blocked for 1 sec\n}\n\n\u002F\u002F Fix: replace synchronized with ReentrantLock:\nlock.lock();\ntry {\n    Thread.sleep(1000); \u002F\u002F virtual thread parks correctly — carrier freed\n} finally {\n    lock.unlock();\n}\n```\n\nDetect pinning with: `-Djdk.tracePinnedThreads=full` (logs whenever\npinning occurs). Java 24+ is improving `synchronized` to avoid pinning\nin most cases.\n\n**Rule of thumb:** replace `synchronized` blocks that contain blocking\ncalls with `ReentrantLock`; avoid long-running native method calls on\nvirtual threads.\n",{"id":411,"difficulty":153,"q":412,"a":413},"threadlocal-virtual-threads","How does ThreadLocal behave with virtual threads and what is the alternative?","`ThreadLocal` works with virtual threads but has two problems at scale:\n\n1. If each virtual thread (potentially millions) initialises a heavy\n   `ThreadLocal` (e.g., a database connection), memory usage explodes.\n2. Thread pools reuse threads, so `ThreadLocal` values survive across\n   tasks — with virtual threads there's no pooling, so values don't leak\n   *between* tasks, but the allocation-per-thread cost remains.\n\nJava 20 introduced **Scoped Values** (`ScopedValue`, JEP 446 — finalized\nin Java 21) as the preferred alternative for virtual threads:\n\n```java\n\u002F\u002F ThreadLocal — allocated per virtual thread:\nprivate static final ThreadLocal\u003CUser> CURRENT_USER = new ThreadLocal\u003C>();\n\n\u002F\u002F ScopedValue — immutable, inherited by child threads, no per-thread alloc:\nprivate static final ScopedValue\u003CUser> CURRENT_USER = ScopedValue.newInstance();\n\nScopedValue.where(CURRENT_USER, user).run(() -> {\n    \u002F\u002F CURRENT_USER.get() returns 'user' within this scope\n    processRequest();\n});\n```\n\n`ScopedValue` is immutable and automatically cleaned up when the scope\nexits — no remove() needed, no accidental cross-task contamination.\n\n**Rule of thumb:** prefer `ScopedValue` over `ThreadLocal` for new code\non Java 21+, especially context propagation (user, trace-id, locale).\n",{"id":415,"difficulty":153,"q":416,"a":417},"structured-concurrency","What is structured concurrency and how does it relate to virtual threads?","**Structured concurrency** (Java 21 preview, JEP 453) ensures that when a\ntask spawns subtasks, their lifetimes are bounded by the parent task's\nscope. If the parent fails or is cancelled, subtasks are automatically\ncancelled — no orphaned threads.\n\n```java\ntry (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n    Future\u003CString> user    = scope.fork(() -> fetchUser(id));\n    Future\u003CList\u003COrder>> orders = scope.fork(() -> fetchOrders(id));\n\n    scope.join();           \u002F\u002F wait for both\n    scope.throwIfFailed();  \u002F\u002F propagate first failure\n\n    return new Response(user.resultNow(), orders.resultNow());\n}\n\u002F\u002F Scope exits → both subtasks guaranteed to be done or cancelled\n```\n\n`ShutdownOnFailure` cancels the remaining subtasks if one fails.\n`ShutdownOnSuccess` returns as soon as any subtask succeeds (useful for\nparallel search \u002F hedging).\n\n**Rule of thumb:** structured concurrency + virtual threads = simple\nsynchronous-looking code that is safe, cancellable, and leak-free.\n",{"id":419,"difficulty":35,"q":420,"a":421},"virtual-threads-not-for-cpu","When should you NOT use virtual threads?","Virtual threads are optimised for **I\u002FO-bound** work. They are not\nappropriate for:\n\n- **CPU-bound tasks** — a CPU-heavy virtual thread still occupies a\n  carrier thread for its entire run; many CPU-bound virtual threads compete\n  for the same carrier pool, starving each other. Use platform threads\n  in a `ForkJoinPool` or `newFixedThreadPool` instead.\n- **Code that relies on thread identity** — some frameworks or libraries\n  assume thread-pool reuse of `ThreadLocal`. Virtual threads don't pool,\n  so thread-identity assumptions break.\n- **Heavy synchronized sections over blocking I\u002FO** — pinning turns\n  virtual threads into expensive platform threads during those sections.\n\n```java\n\u002F\u002F Bad — CPU-bound on virtual threads starves the carrier pool:\nvar executor = Executors.newVirtualThreadPerTaskExecutor();\nexecutor.submit(() -> computePrimes(10_000_000)); \u002F\u002F blocks a carrier\n\n\u002F\u002F Better — CPU work on platform threads:\nvar pool = ForkJoinPool.commonPool();\npool.submit(() -> computePrimes(10_000_000));\n```\n\n**Rule of thumb:** virtual threads excel at I\u002FO-bound concurrent tasks;\nstick to platform thread pools for CPU-intensive computation.\n",{"id":423,"difficulty":43,"q":424,"a":425},"creating-virtual-threads","What are the different ways to create virtual threads in Java?","Java 21 provides several APIs for creating virtual threads:\n\n```java\n\u002F\u002F 1. Thread.ofVirtual() builder:\nThread vt = Thread.ofVirtual()\n                  .name(\"my-virtual-thread\")\n                  .start(myTask);\n\n\u002F\u002F 2. Thread.startVirtualThread() — shorthand:\nThread vt2 = Thread.startVirtualThread(myTask);\n\n\u002F\u002F 3. ExecutorService — one virtual thread per task:\ntry (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {\n    exec.submit(task1);\n    exec.submit(task2);\n} \u002F\u002F waits for all tasks on close\n\n\u002F\u002F 4. ThreadFactory for frameworks:\nThreadFactory factory = Thread.ofVirtual().factory();\n```\n\nSpring Boot 3.2+ and Tomcat 10.1.x+ support virtual threads with a single\nproperty: `spring.threads.virtual.enabled=true`.\n\n**Rule of thumb:** for servers, use `newVirtualThreadPerTaskExecutor()`\nor the framework's built-in virtual thread support; avoid creating raw\nvirtual threads manually in application code.\n",{"id":427,"difficulty":35,"q":428,"a":429},"virtual-threads-observability","How do you observe and debug virtual threads?","Standard observability tools work with virtual threads in Java 21+:\n\n```bash\n# Thread dump — shows virtual threads:\njcmd \u003Cpid> Thread.dump_to_file -format=json \u002Ftmp\u002Fthreads.json\n\n# JFR events — virtual thread mount\u002Funmount, pinning:\njava -XX:StartFlightRecording=filename=recording.jfr MyApp\n\n# Detect pinning at runtime:\njava -Djdk.tracePinnedThreads=full MyApp\n```\n\n`jstack` was updated to show virtual threads. JDK Flight Recorder (JFR)\nhas dedicated events for virtual thread lifecycle and pinning incidents.\n\nIn thread dumps, virtual threads are grouped and summarised by stack trace\nif many share the same waiting state (e.g., 50,000 threads all blocked on\nJDBC).\n\n**Rule of thumb:** enable JFR in production for virtual-thread apps;\nthe pinning events are the most important signal to watch for performance\nregressions.\n",{"id":431,"difficulty":35,"q":432,"a":433},"virtual-threads-migration","How do you migrate an existing thread-pool application to virtual threads?","For most Spring\u002FJakarta EE applications, migration is a single config\nchange. For manual migration:\n\n```java\n\u002F\u002F Before — fixed platform thread pool:\nExecutorService executor = Executors.newFixedThreadPool(200);\n\n\u002F\u002F After — virtual thread per task:\nExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();\n```\n\nSteps:\n1. Replace pool creation — swap to `newVirtualThreadPerTaskExecutor()`.\n2. Remove artificial pool-size tuning (thread count limits are no longer\n   meaningful for I\u002FO-bound tasks).\n3. Audit `synchronized` blocks that contain blocking operations — replace\n   with `ReentrantLock` to avoid pinning.\n4. Replace heavy `ThreadLocal` with `ScopedValue` where context\n   propagation is needed.\n5. Run under load and check JFR for pinning events.\n\n**Rule of thumb:** the migration is usually 1–3 lines for the executor;\nthe real work is finding and fixing pinning in synchronized\u002Fnative code.\n",{"id":435,"difficulty":153,"q":436,"a":437},"virtual-threads-reactive","Do virtual threads replace reactive programming (Project Reactor, RxJava)?","For most applications, virtual threads are a **simpler alternative** to\nreactive stacks for I\u002FO-bound concurrency. Instead of chaining\n`Mono`\u002F`Flux` operators or `CompletableFuture`, you write straightforward\nsynchronous blocking code that scales equally well.\n\n```java\n\u002F\u002F Reactive — non-blocking but complex:\nMono.fromCallable(() -> userRepo.findById(id))\n    .subscribeOn(Schedulers.boundedElastic())\n    .flatMap(user -> orderRepo.findByUser(user.id()))\n    .map(orders -> new Response(user, orders))\n    .subscribe(...);\n\n\u002F\u002F Virtual threads — blocking but simple:\nUser user = userRepo.findById(id);      \u002F\u002F blocks a virtual thread\nList\u003COrder> orders = orderRepo.findByUser(user.id());\nreturn new Response(user, orders);\n```\n\nReactive frameworks like Reactor and RxJava still excel at:\n- **Backpressure** — controlling producer\u002Fconsumer rates.\n- **Streaming** large data sets where you process elements as they arrive.\n- Ecosystems already built on reactive (Vert.x, Quarkus reactive).\n\n**Rule of thumb:** for new I\u002FO-bound services, prefer virtual threads +\nsynchronous code over reactive; keep reactive where backpressure or\nstreaming semantics are genuinely needed.\n",{"id":439,"difficulty":35,"q":440,"a":441},"virtual-thread-identity","How can you check at runtime if a thread is a virtual thread?","Java 21 added `Thread.isVirtual()`:\n\n```java\nThread t = Thread.currentThread();\nSystem.out.println(t.isVirtual()); \u002F\u002F true if running on a virtual thread\n\n\u002F\u002F Also available as a static check:\nif (Thread.currentThread().isVirtual()) {\n    \u002F\u002F avoid synchronized blocks with blocking I\u002FO here\n}\n```\n\nVirtual threads also have distinct characteristics:\n- `isDaemon()` always returns `true`.\n- `getPriority()` is always `NORM_PRIORITY` (5) and cannot be changed.\n- They are not in any `ThreadGroup` that is meaningful to the application.\n\n**Rule of thumb:** `Thread.isVirtual()` is mainly useful in libraries\nand frameworks that need to adapt behaviour based on whether the caller\nis using virtual threads.\n",13,{"description":32},"Java virtual threads interview questions — Project Loom, platform vs virtual threads, carrier threads, structured concurrency, thread-per-request model, pinning, ThreadLocal, when to use virtual threads, and migration from thread pools.","java\u002Fmodern-java\u002Fvirtual-threads","CJ5_11SmZBZ-4Y7VslMrt2_RJxor76bAmXrfb5bqjk4",{"id":448,"title":449,"body":450,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":454,"navigation":38,"order":455,"path":456,"questions":457,"questionsCount":502,"related":103,"seo":503,"seoDescription":504,"stem":505,"subtopic":449,"topic":19,"topicSlug":21,"updated":107,"__hash__":506},"qa\u002Fjava\u002Fmodern-java\u002Frecord-patterns.md","Record Patterns",{"type":29,"value":451,"toc":452},[],{"title":32,"searchDepth":33,"depth":33,"links":453},[],{},7,"\u002Fjava\u002Fmodern-java\u002Frecord-patterns",[458,462,466,470,474,478,482,486,490,494,498],{"id":459,"difficulty":43,"q":460,"a":461},"what-are-record-patterns","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":463,"difficulty":43,"q":464,"a":465},"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":467,"difficulty":35,"q":468,"a":469},"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":471,"difficulty":43,"q":472,"a":473},"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":475,"difficulty":153,"q":476,"a":477},"record-patterns-with-generics","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":479,"difficulty":35,"q":480,"a":481},"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":483,"difficulty":35,"q":484,"a":485},"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":487,"difficulty":35,"q":488,"a":489},"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":491,"difficulty":43,"q":492,"a":493},"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":495,"difficulty":35,"q":496,"a":497},"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":499,"difficulty":43,"q":500,"a":501},"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,{"description":32},"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","LY-WRR4LYHVxfjMQETGKTfLJWYOmwFd-4645hikOe2A",{"id":508,"title":509,"body":510,"description":32,"difficulty":43,"extension":36,"framework":10,"frameworkSlug":8,"meta":514,"navigation":38,"order":515,"path":516,"questions":517,"questionsCount":502,"related":103,"seo":562,"seoDescription":563,"stem":564,"subtopic":509,"topic":19,"topicSlug":21,"updated":107,"__hash__":565},"qa\u002Fjava\u002Fmodern-java\u002Fsequenced-collections.md","Sequenced Collections",{"type":29,"value":511,"toc":512},[],{"title":32,"searchDepth":33,"depth":33,"links":513},[],{},8,"\u002Fjava\u002Fmodern-java\u002Fsequenced-collections",[518,522,526,530,534,538,542,546,550,554,558],{"id":519,"difficulty":43,"q":520,"a":521},"what-are-sequenced-collections","What are sequenced collections and why were they added in Java 21?","**Sequenced collections** (Java 21, JEP 431) introduce three new\ninterfaces — `SequencedCollection`, `SequencedSet`, and `SequencedMap` —\nthat give a **uniform API for accessing the first and last elements** and\nfor **iterating in reverse order** across all ordered collection types.\n\nBefore Java 21 each collection had its own non-uniform way to get the\nfirst or last element:\n\n```java\nList\u003CString> list = List.of(\"a\", \"b\", \"c\");\nlist.get(0);                           \u002F\u002F first element\nlist.get(list.size() - 1);             \u002F\u002F last element — verbose\n\nDeque\u003CString> deque = new ArrayDeque\u003C>(list);\ndeque.peekFirst();                     \u002F\u002F first\ndeque.peekLast();                      \u002F\u002F last\n\nSortedSet\u003CString> sorted = new TreeSet\u003C>(list);\nsorted.first();                        \u002F\u002F first\nsorted.last();                         \u002F\u002F last\n```\n\n`SequencedCollection` unifies these with `getFirst()` \u002F `getLast()`.\n\n**Rule of thumb:** if you need the first or last element, check whether\nthe collection is a `SequencedCollection` and use the new API.\n",{"id":523,"difficulty":43,"q":524,"a":525},"sequenced-collection-interface","What methods does SequencedCollection add?","`SequencedCollection\u003CE>` extends `Collection\u003CE>` and adds:\n\n| Method | Description |\n|---|---|\n| `getFirst()` | Returns the first element; throws `NoSuchElementException` if empty |\n| `getLast()` | Returns the last element; throws `NoSuchElementException` if empty |\n| `addFirst(E)` | Inserts at the beginning (optional — throws `UnsupportedOperationException` for fixed-size) |\n| `addLast(E)` | Inserts at the end (optional) |\n| `removeFirst()` | Removes and returns the first element (optional) |\n| `removeLast()` | Removes and returns the last element (optional) |\n| `reversed()` | Returns a reversed view of the collection |\n\n```java\nList\u003CString> list = new ArrayList\u003C>(List.of(\"a\", \"b\", \"c\"));\n\nSystem.out.println(list.getFirst()); \u002F\u002F \"a\"\nSystem.out.println(list.getLast());  \u002F\u002F \"c\"\n\nlist.addFirst(\"z\");                  \u002F\u002F [\"z\", \"a\", \"b\", \"c\"]\nlist.addLast(\"x\");                   \u002F\u002F [\"z\", \"a\", \"b\", \"c\", \"x\"]\n\nlist.removeFirst();                  \u002F\u002F \"z\" removed\nlist.removeLast();                   \u002F\u002F \"x\" removed\n```\n\n**Rule of thumb:** `getFirst()`\u002F`getLast()` replace `get(0)` and\n`get(list.size()-1)` — use them for cleaner, more readable code.\n",{"id":527,"difficulty":43,"q":528,"a":529},"sequenced-set-interface","What is SequencedSet and which classes implement it?","`SequencedSet\u003CE>` extends both `SequencedCollection\u003CE>` and `Set\u003CE>`,\nadding no new methods but guaranteeing both the set contract (no\nduplicates) and a defined encounter order.\n\nImplementing classes:\n- `LinkedHashSet` — insertion-ordered set, implements `SequencedSet`\n- `SortedSet` subtypes: `TreeSet` — sorted, implements `SequencedSet`\n  via `SortedSet` (which extends `SequencedSet`)\n\n```java\nSequencedSet\u003CString> set = new LinkedHashSet\u003C>(List.of(\"b\", \"a\", \"c\"));\nSystem.out.println(set.getFirst()); \u002F\u002F \"b\" — insertion order\nSystem.out.println(set.getLast());  \u002F\u002F \"c\"\nSystem.out.println(set.reversed()); \u002F\u002F [\"c\", \"a\", \"b\"] — reversed view\n```\n\n**Rule of thumb:** `SequencedSet` lets you treat `LinkedHashSet` and\n`TreeSet` uniformly when you need first\u002Flast\u002Freversed without knowing\nthe concrete type.\n",{"id":531,"difficulty":35,"q":532,"a":533},"sequenced-map-interface","What is SequencedMap and what methods does it add?","`SequencedMap\u003CK,V>` extends `Map\u003CK,V>` and adds methods to access\nthe first and last entries:\n\n| Method | Description |\n|---|---|\n| `firstEntry()` | Returns (but does not remove) the first `Map.Entry` |\n| `lastEntry()` | Returns the last `Map.Entry` |\n| `pollFirstEntry()` | Removes and returns the first entry |\n| `pollLastEntry()` | Removes and returns the last entry |\n| `putFirst(K, V)` | Inserts\u002Fmoves entry to the front (optional) |\n| `putLast(K, V)` | Inserts\u002Fmoves entry to the back (optional) |\n| `reversed()` | Returns a reversed-order map view |\n| `sequencedKeySet()` | Returns a `SequencedSet\u003CK>` |\n| `sequencedValues()` | Returns a `SequencedCollection\u003CV>` |\n| `sequencedEntrySet()` | Returns a `SequencedSet\u003CMap.Entry\u003CK,V>>` |\n\n```java\nSequencedMap\u003CString, Integer> map = new LinkedHashMap\u003C>();\nmap.put(\"a\", 1); map.put(\"b\", 2); map.put(\"c\", 3);\n\nSystem.out.println(map.firstEntry()); \u002F\u002F a=1\nSystem.out.println(map.lastEntry());  \u002F\u002F c=3\n\nmap.putFirst(\"z\", 0);                \u002F\u002F {z=0, a=1, b=2, c=3}\n```\n\n**Rule of thumb:** `SequencedMap` lets you use `LinkedHashMap` and\n`TreeMap` with a uniform first\u002Flast\u002Freversed API without casting.\n",{"id":535,"difficulty":43,"q":536,"a":537},"which-collections-implement","Which existing Java collections implement the new sequenced interfaces?","The new interfaces are retrofitted onto the existing type hierarchy:\n\n| Collection | Implements |\n|---|---|\n| `ArrayList`, `LinkedList`, `ArrayDeque` | `SequencedCollection` |\n| `LinkedHashSet` | `SequencedSet` |\n| `TreeSet` | `SequencedSet` (via `SortedSet`) |\n| `LinkedHashMap` | `SequencedMap` |\n| `TreeMap` | `SequencedMap` (via `SortedMap`) |\n| `List` (interface) | `SequencedCollection` |\n| `Deque` (interface) | `SequencedCollection` |\n| `SortedSet` (interface) | `SequencedSet` |\n| `SortedMap` (interface) | `SequencedMap` |\n\n```java\nSequencedCollection\u003CInteger> col = new ArrayDeque\u003C>(List.of(1, 2, 3));\ncol.getFirst(); \u002F\u002F 1\ncol.getLast();  \u002F\u002F 3\n```\n\n**Rule of thumb:** if the concrete type is `ArrayList`, `LinkedHashSet`,\n`TreeMap`, etc. you already have the new methods — no migration needed.\n",{"id":539,"difficulty":35,"q":540,"a":541},"reversed-method","What does the reversed() method return?","`reversed()` returns a **live, write-through view** of the collection in\nreverse encounter order. Mutations through the reversed view are reflected\nin the original, and vice versa.\n\n```java\nList\u003CString> list = new ArrayList\u003C>(List.of(\"a\", \"b\", \"c\"));\nList\u003CString> rev  = list.reversed();\n\nSystem.out.println(rev);   \u002F\u002F [c, b, a]\n\nrev.addFirst(\"z\");         \u002F\u002F adds to the reversed view's front\nSystem.out.println(list);  \u002F\u002F [a, b, c, z]  — \"z\" is at the END of original\n\nlist.add(\"x\");             \u002F\u002F adds to original\nSystem.out.println(rev);   \u002F\u002F [x, z, c, b, a] — reflected in reversed view\n```\n\n**Rule of thumb:** `reversed()` is a live view, not a copy — mutations\npropagate both ways; if you need an independent copy, copy it explicitly.\n",{"id":543,"difficulty":43,"q":544,"a":545},"list-getfirst-vs-get0","Is list.getFirst() just a synonym for list.get(0)?","For `List` implementations, `getFirst()` is equivalent to `get(0)` except\nfor the exception when the list is empty. `get(0)` throws\n`IndexOutOfBoundsException`; `getFirst()` throws `NoSuchElementException`.\n\n```java\nList\u003CString> empty = new ArrayList\u003C>();\n\nempty.get(0);      \u002F\u002F IndexOutOfBoundsException: Index 0 out of bounds\nempty.getFirst();  \u002F\u002F NoSuchElementException\n\nList\u003CString> list = List.of(\"a\", \"b\", \"c\");\nlist.get(0);       \u002F\u002F \"a\"\nlist.getFirst();   \u002F\u002F \"a\" — same result\n```\n\nThe semantic difference: `IndexOutOfBoundsException` signals a programming\nerror (index miscalculation); `NoSuchElementException` signals an empty\ncollection. `getFirst()` better expresses intent.\n\n**Rule of thumb:** prefer `getFirst()` \u002F `getLast()` over `get(0)` and\n`get(size-1)` — the intent is clearer and the exception type is more\nsemantically correct.\n",{"id":547,"difficulty":35,"q":548,"a":549},"sequenced-vs-deque","How does SequencedCollection relate to Deque?","`Deque` already had `peekFirst()`\u002F`peekLast()`, `pollFirst()`\u002F`pollLast()`,\n`addFirst()`\u002F`addLast()`. The new `SequencedCollection` essentially makes\nthese methods available on all ordered collections uniformly:\n\n| Deque method | SequencedCollection equivalent |\n|---|---|\n| `peekFirst()` | `getFirst()` |\n| `peekLast()` | `getLast()` |\n| `pollFirst()` | `removeFirst()` |\n| `pollLast()` | `removeLast()` |\n| `addFirst(e)` | `addFirst(e)` |\n| `addLast(e)` | `addLast(e)` |\n\nDeque itself now extends `SequencedCollection`, so `ArrayDeque` and\n`LinkedList` implement both and can be used interchangeably with either API.\n\n**Rule of thumb:** `Deque` and `SequencedCollection` now share the first\u002F\nlast API — prefer `SequencedCollection` in method signatures when you don't\nneed `Deque`-specific operations like `push()`\u002F`pop()`.\n",{"id":551,"difficulty":35,"q":552,"a":553},"unmodifiable-sequenced","Do Collections.unmodifiableList() and List.of() support SequencedCollection?","Yes. `List` now extends `SequencedCollection`, so all `List` instances —\nincluding `List.of()` (immutable), `Collections.unmodifiableList()`, and\n`Arrays.asList()` — have `getFirst()`\u002F`getLast()`:\n\n```java\nList\u003CString> immutable = List.of(\"a\", \"b\", \"c\");\nSystem.out.println(immutable.getFirst()); \u002F\u002F \"a\" — works fine\nSystem.out.println(immutable.getLast());  \u002F\u002F \"c\"\n\n\u002F\u002F Mutation methods throw UnsupportedOperationException on immutable lists:\nimmutable.addFirst(\"z\"); \u002F\u002F UnsupportedOperationException\n```\n\n**Rule of thumb:** `getFirst()`\u002F`getLast()` are safe on any `List`,\nincluding immutable ones; mutation methods follow the same modifiability\nrules as before.\n",{"id":555,"difficulty":43,"q":556,"a":557},"before-java21-workarounds","What were the workarounds for getting first\u002Flast elements before Java 21?","Each collection type needed a different API:\n\n```java\n\u002F\u002F List — verbose index arithmetic:\nString first = list.get(0);\nString last  = list.get(list.size() - 1);\n\n\u002F\u002F LinkedList (as Deque) — dedicated methods:\nString first = ((LinkedList\u003CString>) list).getFirst();\n\n\u002F\u002F ArrayDeque:\nString first = deque.peekFirst();\nString last  = deque.peekLast();\n\n\u002F\u002F SortedSet \u002F TreeSet:\nString first = sortedSet.first();\nString last  = sortedSet.last();\n\n\u002F\u002F Iterating in reverse — no unified API:\nCollections.reverse(list); \u002F\u002F mutates the list!\n\u002F\u002F Or:\nListIterator\u003CString> it = list.listIterator(list.size());\nwhile (it.hasPrevious()) System.out.println(it.previous());\n```\n\n`SequencedCollection` replaces all of these with `getFirst()`, `getLast()`,\nand `reversed()`.\n\n**Rule of thumb:** if you wrote `list.get(list.size()-1)` before Java 21,\nreplace it with `list.getLast()` — it is shorter and communicates intent.\n",{"id":559,"difficulty":35,"q":560,"a":561},"sequenced-in-method-signatures","When should you use SequencedCollection as a method parameter type?","Use `SequencedCollection\u003CE>` (or `SequencedMap\u003CK,V>`) in method signatures\nwhen your method **specifically needs first\u002Flast access or reverse\niteration** but doesn't need to know the concrete type:\n\n```java\n\u002F\u002F Accept any ordered collection with first\u002Flast semantics:\nstatic \u003CT> T middle(SequencedCollection\u003CT> col) {\n    \u002F\u002F Use getFirst\u002FgetLast without caring if it's ArrayList or LinkedHashSet\n    if (col.isEmpty()) throw new NoSuchElementException();\n    \u002F\u002F ... navigate to middle via iterator\n}\n\n\u002F\u002F Too narrow — only works for List:\nstatic \u003CT> T middle(List\u003CT> list) { ... }\n\n\u002F\u002F Too broad — Collection doesn't guarantee order:\nstatic \u003CT> T middle(Collection\u003CT> col) { ... } \u002F\u002F getFirst() doesn't exist\n```\n\n**Rule of thumb:** prefer `SequencedCollection` over `List` in signatures\nwhen order and first\u002Flast access matter but random index access (`get(i)`)\ndoes not.\n",{"description":32},"Java sequenced collections interview questions — SequencedCollection, SequencedSet, SequencedMap, getFirst\u002FgetLast, addFirst\u002FaddLast, reversed(), which collections implement the new interfaces, and migration from workarounds.","java\u002Fmodern-java\u002Fsequenced-collections","Aswq8TvLa3y5unPRdSW-X5Ypb8giKFStzLf2q6t9AmY",1782244097338]