[{"data":1,"prerenderedAt":122},["ShallowReactive",2],{"qa-\u002Fjava\u002Fmodern-java\u002Fsealed-classes":3},{"page":4,"siblings":89,"blog":119},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":80,"related":81,"seo":82,"seoDescription":83,"stem":84,"subtopic":6,"topic":85,"topicSlug":86,"updated":87,"__hash__":88},"qa\u002Fjava\u002Fmodern-java\u002Fsealed-classes.md","Sealed Classes",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,"\u002Fjava\u002Fmodern-java\u002Fsealed-classes",[22,27,31,35,39,43,47,51,55,60,64,68,72,76],{"id":23,"difficulty":24,"q":25,"a":26},"what-are-sealed-classes","easy","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":28,"difficulty":24,"q":29,"a":30},"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":32,"difficulty":14,"q":33,"a":34},"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":36,"difficulty":14,"q":37,"a":38},"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":40,"difficulty":24,"q":41,"a":42},"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":44,"difficulty":14,"q":45,"a":46},"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":48,"difficulty":14,"q":49,"a":50},"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":52,"difficulty":14,"q":53,"a":54},"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":56,"difficulty":57,"q":58,"a":59},"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":61,"difficulty":57,"q":62,"a":63},"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":65,"difficulty":14,"q":66,"a":67},"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":69,"difficulty":14,"q":70,"a":71},"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":73,"difficulty":24,"q":74,"a":75},"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":77,"difficulty":14,"q":78,"a":79},"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,null,{"description":11},"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","Modern Java","modern-java","2026-06-20","9QpfaTliEY2U89RE3zmAYqNt6DxbvRpm8_UelBaYDP4",[90,94,95,99,103,107,111,115],{"subtopic":91,"path":92,"order":93},"Records","\u002Fjava\u002Fmodern-java\u002Frecords",1,{"subtopic":6,"path":20,"order":12},{"subtopic":96,"path":97,"order":98},"Switch Pattern Matching","\u002Fjava\u002Fmodern-java\u002Fswitch-pattern-matching",3,{"subtopic":100,"path":101,"order":102},"Text Blocks","\u002Fjava\u002Fmodern-java\u002Ftext-blocks",4,{"subtopic":104,"path":105,"order":106},"instanceof Pattern Matching","\u002Fjava\u002Fmodern-java\u002Finstanceof-pattern-matching",5,{"subtopic":108,"path":109,"order":110},"Virtual Threads","\u002Fjava\u002Fmodern-java\u002Fvirtual-threads",6,{"subtopic":112,"path":113,"order":114},"Record Patterns","\u002Fjava\u002Fmodern-java\u002Frecord-patterns",7,{"subtopic":116,"path":117,"order":118},"Sequenced Collections","\u002Fjava\u002Fmodern-java\u002Fsequenced-collections",8,{"path":120,"title":121},"\u002Fblog\u002Fjava-sealed-classes","Java Sealed Classes — Closed Hierarchies, Exhaustive Switch, and ADTs",1782244117550]