密封类要求直接子类必须显式声明final/sealed或non-sealed,以保障编译期可穷举性与模式匹配全覆盖;未声明则编译报错,确保继承图谱静态可分析。

密封类(sealed class)的核心设计意图是**显式控制其继承关系的边界**——它不希望子类被任意扩展,而是要求所有直接子类都必须在编译期就明确、可枚举。因此,Java(自15预览、17正式支持)和 Kotlin(自1.5)、C#(9.0+)等语言都规定:密封类的**直接子类必须显式声明为 final、sealed 或 non-sealed**。这不是语法限制,而是类型安全与模式匹配可靠性的基础保障。
为什么不能默认继承?
如果允许子类像普通类一样“默默继承”,就会破坏密封类的封闭性语义:
- 编译器无法确认是否已穷举所有可能的子类型 → 模式匹配(如
switch表达式)无法做到“全覆盖检查” - 其他模块可能悄悄新增子类 → 密封类作者失去对类型演化的掌控力
- API 的契约变得模糊:调用方本以为只有 A/B/C 三种情况,结果运行时冒出 D
final / sealed:彻底终止继承链
这是最常见也最安全的选择。表示该子类本身不可再被继承:
- Java 中写
final class Circle extends Shape { ... } - C# 中写
sealed class Circle : Shape { ... } - Kotlin 中写
class Circle : Shape() {}(Kotlin 默认 final,无需额外关键字)
适合那些语义上“已是最终形态”的类型,比如具体几何图形、固定状态枚举值等。
non-sealed:有限开放,但需主动授权
允许该子类被进一步继承,但仅限于同一模块/包内,并且后续子类仍要遵守密封规则:
- Java 中必须显式写
non-sealed class ColoredShape extends Shape { ... } - 后续子类(如
RedCircle)仍须声明为final或non-sealed - 跨模块继承默认被禁止,除非模块声明了
opens或exports相应包
适用于需要分层建模的场景,例如:形状 → 彩色形状 → 红色圆形,但红色圆形不能再变种。
不声明会怎样?编译直接报错
所有主流语言都把“未显式修饰的密封子类”视为错误:
- Java 编译器报错:
error: non-sealed type must be explicitly declared as non-sealed - C# 报错:
CS8861: A sealed class cannot derive from a sealed class unless it is also sealed(若父类 sealed 但子类没 seal) - Kotlin 报错:
Class 'X' is not allowed to inherit from sealed class 'Y'
这确保了密封性从定义到使用全程可验证,不是靠约定,而是靠编译器强制。
本质上,final、sealed、non-sealed 是同一枚硬币的三面:它们共同构成一个**可静态分析的继承图谱**,让类型系统知道“哪些类型是终点,哪些还能往下延展,延展的权限又在哪里”。没有这种显式声明,密封类就失去了存在的意义。










