skip to content

What is a sealed class or interface in Java, and what problem does it solve?

level: juniorimportance: must knowfreq 72%

answer

  1. Closed set of subtypes, not open and not final
  2. sealed + permits clause
  3. Enables exhaustive switch with no default
  4. Java 17 permanent feature
  5. Middle ground between final and open

basics

~10 s

A sealed class or interface restricts which other classes can extend or implement it. You list the allowed subtypes with the permits clause, so no unexpected class can join the hierarchy.

solid answer

~40 s

A sealed type (class or interface, since Java 17) lets the author of a type control exactly which classes may extend or implement it, using the sealed modifier plus an optional permits clause naming the allowed subtypes. Before sealed types you had two blunt choices: final (no subtypes at all) or open (anyone, anywhere can extend). Sealed gives a middle ground: a known, closed set of subtypes. This makes a hierarchy a true algebraic 'sum type' - the compiler knows the complete list of alternatives. That enables exhaustive switch over the permitted subtypes (no default branch needed), safer modelling of domain states, and stronger encapsulation because library authors prevent outsiders from injecting rogue subclasses.

go deeper

for a junior

Can state that sealed restricts which classes may extend/implement a type via permits, and that it sits between final and fully open.

for a middle

Explains the exhaustive-switch benefit, knows permits can be inferred for same-file subtypes, and gives a real modelling example.

for a senior

Frames sealed types as algebraic sum types, connects them to pattern matching and API encapsulation, and discusses when to choose sealed over an enum or open hierarchy.

for a principal

Reasons about sealed types in API/module design: stability of the permitted set as a public contract, evolution risk of adding a subtype, and trade-offs versus visitor/enum patterns across module boundaries.

## The problem In Java, a normal (non-final) class or interface is **open for extension**: any class, anywhere, can `extend` or `implement` it. Sometimes that is good (frameworks rely on it). But often a type author wants a **fixed, known set of subtypes** and nothing else. Historically you only had two extremes: - `final class Foo` - **nobody** can subclass it. Too restrictive if you legitimately want a few subtypes. - `class Foo` - **anybody** can subclass it. Too permissive; you lose control and you can never know the full list. A **sealed type** (added as a permanent feature in **Java 17**, previewed in 16) fills the gap: it stays extensible, but **only by an explicitly listed set of types**. ## Syntax ```java public sealed interface Shape permits Circle, Square, Rectangle {} ``` - `sealed` is a modifier on the class/interface. - `permits X, Y, Z` lists the **only** types allowed to directly extend/implement it. The `permits` clause can be **omitted** if all permitted subtypes are in the **same source file** - the compiler infers them. ## Why it matters 1. **Closed set = algebraic sum type.** A sealed hierarchy says 'a Shape is *exactly one of* Circle, Square, or Rectangle.' The compiler now knows the complete universe of alternatives. 2. **Exhaustive `switch`.** Because the set is closed, a `switch` over a sealed type that covers every permitted subtype is **exhaustive** - the compiler accepts it with **no `default` branch**, and will give a compile error if you later add a subtype and forget to handle it. This is the single biggest practical win, especially combined with **pattern matching** for `switch` and record patterns. 3. **Encapsulation / API control.** A library author can publish a type that the outside world can *use* but **cannot extend with rogue implementations** - the permitted list is fixed at compile time. 4. **Better domain modelling.** States like `Loading | Loaded | Failed`, expression trees, results, etc. map cleanly to sealed hierarchies. ## Key terms defined - **Subtype / subclass:** a type that `extends` (for classes) or `implements` (for interfaces) another. - **Permitted subtype:** a type named in the `permits` clause (or inferred); the only ones allowed. - **Exhaustive switch:** a `switch` whose branches cover every possible value, so no `default` is needed. - **Sum type / algebraic data type:** a type defined as 'one of a fixed set of alternatives'. ## Contrast at a glance | Modifier | Who can subtype | |---|---| | `final` | nobody | | `sealed` | only the permitted list | | (none) | anybody | | `non-sealed` | reopens the branch to anybody again | Sealed types are a **modelling and safety** feature first; they do not change runtime performance meaningfully - the benefit is at compile time.

  • Can the permits clause be omitted?
    Yes - if every permitted subtype lives in the same source file (or same compilation unit), the compiler infers the permitted list, so you can write just sealed without permits.
  • In which Java version did sealed classes become a permanent (non-preview) feature?
    Java 17 (previewed in Java 15 and 16).

A sealed type is like a guest list at a private event: it's not locked (final) and not open to the public (no modifier) - only the named invitees may enter.

saying these in an interview costs you the question

  • Saying sealed means 'cannot be subclassed at all' - that is final; sealed allows a controlled set
  • Claiming sealed gives a runtime performance boost - the benefit is compile-time safety and modelling
  • Thinking permits is always mandatory - it can be inferred when subtypes are in the same file

context