Every direct subtype of a sealed class must itself declare one of three modifiers - which three, and what does each mean?
answer
- final / sealed / non-sealed - pick exactly one
- final = chain stops
- sealed = continue closed, needs its own permits
- non-sealed = branch reopens to everyone
- records are implicitly final
basics
~10 sEach permitted subclass must be final (no further subclasses), sealed (continues the closed hierarchy with its own permits list), or non-sealed (reopens that branch so anyone can extend it).
solid answer
~40 sA permitted subtype of a sealed class or interface is required by the compiler to explicitly choose how *it* continues the hierarchy, using exactly one of three modifiers. final means the subtype closes the chain - nothing can extend it further. sealed means the subtype is itself a sealed type and must declare its own permits list, continuing the controlled hierarchy one level deeper. non-sealed deliberately reopens that branch: it removes the sealing constraint, so any class may once again extend it without being listed anywhere. This rule exists so the 'closedness' of a sealed hierarchy is explicit at every level - a reader can see exactly where the closed set ends and where (if anywhere) it becomes open again. Records are implicitly final, so a record permitted subtype satisfies the final requirement automatically.
code
java · 16 linespublic sealed interface Shape
permits Circle, Polygon, CustomShape {}
// 1) final: chain stops here
public record Circle(double radius) implements Shape {} // implicitly final
// 2) sealed: continue the closed hierarchy with its own permits
public sealed interface Polygon extends Shape
permits Triangle, Square {}
public record Triangle(double a, double b, double c) implements Polygon {}
public record Square(double side) implements Polygon {}
// 3) non-sealed: reopen this branch to anyone
public non-sealed interface CustomShape extends Shape {}
// now ANY class may implement CustomShape, e.g. a third party:
class MyWeirdShape implements CustomShape {}go deeper
Can name the three modifiers (final, sealed, non-sealed) and that one is mandatory on each permitted subtype.
Explains what each modifier means for further extension and knows records are implicitly final so they satisfy the final case.
Articulates why the explicit-choice rule exists (closedness must be visible at every level) and uses non-sealed deliberately as an escape hatch in API design.
Evaluates the maintainability and contract implications of each branch choice, e.g. when non-sealed is a code smell that undermines exhaustiveness versus a deliberate extension point for clients.
## The rule When a class or interface is `sealed`, the compiler does **not** let its permitted subtypes be 'plain'. **Every direct permitted subtype must explicitly declare one of exactly three modifiers:** ### 1. `final` The subtype **closes the chain**: nothing may extend it further. The hierarchy stops here. ```java public sealed interface Shape permits Circle, Square {} public final class Circle implements Shape {} ``` ### 2. `sealed` The subtype is itself a sealed type and **must declare its own `permits`** (or rely on same-file inference). The closed hierarchy **continues one level deeper**. ```java public sealed interface Shape permits Polygon, Circle {} public sealed interface Polygon extends Shape permits Triangle, Square {} public final class Triangle implements Polygon {} public final class Square implements Polygon {} public final class Circle implements Shape {} ``` ### 3. `non-sealed` The subtype **deliberately reopens** the branch. It drops the sealing constraint, so **any class may extend it** again, with no need to appear in any `permits` list. ```java public sealed interface Shape permits Circle, CustomShape {} public non-sealed interface CustomShape extends Shape {} // anyone may implement CustomShape now ``` ## Why the rule exists The whole value of sealing is a **known, closed set**. If a permitted subtype could be silently re-extended by anyone, the closedness would leak away invisibly. By **forcing an explicit choice** at every level, Java makes the boundary of the closed set **visible in the source**: a reader sees exactly where the set is final, where it keeps narrowing (`sealed`), and where it intentionally opens back up (`non-sealed`). ## Important details - **Exactly one** of the three is required on each direct permitted subtype - you cannot omit it, and you cannot combine `final` with `non-sealed` (they are contradictory). - **Records are implicitly `final`.** So a `record` that is a permitted subtype already satisfies the `final` requirement - you do **not** add (and cannot add) another modifier. This is why sealed-interface + record combinations are so common. - `non-sealed` is the only Java keyword with a hyphen. - The choice is per-direct-subtype; different branches can choose differently (one `final`, one `sealed`, one `non-sealed`). ## Terms defined - **Permitted subtype:** a type listed in (or inferred for) the `permits` clause. - **Closing the chain:** making further extension impossible (`final`). - **Reopening:** removing the sealing constraint so arbitrary extension is allowed again (`non-sealed`).
- If a permitted subtype is a record, which modifier do you write?None - records are implicitly final, which already satisfies the requirement, and you cannot add another modifier.
- What does non-sealed accomplish?It reopens that branch of the hierarchy: any class may extend the non-sealed type without being listed in any permits clause, removing the closed-set guarantee for that subtree.
saying these in an interview costs you the question
- Saying a permitted subtype can just be a plain class with no modifier - it must explicitly be final, sealed, or non-sealed
- Thinking non-sealed still requires listing extenders - it explicitly removes that constraint
- Adding final to a record subtype (records are already implicitly final)
- Believing you can mark a subtype both final and non-sealed