Detailed explanation
In Java 17, final and sealed control inheritance at different levels. A final class cannot have any subclasses: final class Money {} prevents another class from extending Money. A sealed class allows inheritance, but restricts its direct subclasses to an approved set. Sealed classes and interfaces became a standard feature in Java 17 through JEP 409. The central distinction is therefore no inheritance versus controlled inheritance, not immutable versus mutable objects.
For example, sealed interface Shape permits Circle, Square {} allows only Circle and Square to directly implement Shape. Each permitted type must directly extend or implement the sealed type. A permitted subclass must be final, sealed, or non-sealed: final closes that branch, sealed restricts its next level, and non-sealed allows unrestricted further inheritance. A permitted subinterface uses sealed or non-sealed because interfaces cannot be final. Records work naturally as leaf implementations because they are implicitly final. For instance, record Circle(double radius) implements Shape {} requires no explicit final modifier.
Permitted types must belong to the same named module as the sealed parent; when using the unnamed module, they must belong to the same package. The permits clause may be omitted when the compiler can infer the direct subclasses from declarations in the same compilation unit. Under the hood, a sealed type’s class file contains a PermittedSubclasses attribute. Both compilation and JVM class loading enforce the restriction, so sealing is more than a source-code convention. It controls direct inheritance; a non-sealed branch can still introduce arbitrarily many indirect subtypes.
Use final when subclassing should never be supported, and sealed when modelling a controlled family such as expression nodes or payment types. Final prevents subclasses from altering behaviour, but does not itself make fields or referenced objects immutable. Sealed hierarchies also support exhaustive pattern switches, although pattern matching for switch is preview functionality in Java 17 and becomes final in Java 21. With an exhaustive subtype switch and no catch-all branch, adding an uncovered permitted subtype causes a compilation error when that switch is recompiled. Interviewers typically look for these boundaries, especially the modifier rules, module restrictions, and Java-version distinction.
Key points to remember
- final prohibits all subclasses; sealed restricts direct subclasses to permitted types.
- Permitted subclasses must be final, sealed, or non-sealed; records are implicitly final.
- Permitted types share the parent’s named module, or its package in the unnamed module.
- The compiler and JVM enforce sealing using the PermittedSubclasses class-file attribute.
- Sealed types are standard in Java 17; pattern matching for switch is standard only from Java 21.
Common mistakes candidates make
- Claiming that a final class is automatically immutable; it may still contain mutable state.
- Saying every descendant of a sealed class is known, overlooking the open-ended descendants of a non-sealed branch.
- Presenting pattern switches as a non-preview Java 17 feature or claiming hierarchy changes automatically invalidate already compiled clients.
Likely follow-up questions
Can a class be both sealed and abstract?
Yes: abstract sealed class Expr permits Literal, Add {} is valid. Abstract prevents direct instantiation, while sealed independently restricts direct subclasses.
When should a permitted subclass be non-sealed?
Use non-sealed when one approved branch should remain an extension point. Any eligible class can extend that branch without being listed in the original sealed parent’s permits clause.
Does adding a permitted subtype break existing compiled switches?
It does not automatically trigger compilation errors in already compiled clients. On recompilation, a previously exhaustive switch without a catch-all fails if it leaves the new subtype uncovered; in Java 21, encountering an uncovered subtype in an older compiled exhaustive pattern switch can throw MatchException.
Interview tip: Start with “final forbids inheritance; sealed controls it,” then demonstrate the three permitted subclass modifiers and mention the Java 17 switch-preview caveat.
