如何结合密封类与Switch表达式实现全覆盖无default分支的编译期类型安全校验

作者:袖梨 2026-06-19
使用密封类配合switch表达式可实现编译期穷尽性检查,要求父类用sealed声明并明确permits子类,各子类须在同一模块且声明为final/sealed/non-sealed,switch必须覆盖所有子类及null,when子句不替代case覆盖。

用密封类配合 switch 表达式,能让编译器在编译阶段就确认你是否处理了所有可能的子类型——不需要 default 分支,也能保证逻辑完整、类型安全。

密封类必须定义完整且封闭

这是穷尽性检查的前提。父类型(类或接口)要用 sealed 声明,并通过 permits 明确列出所有允许的直接子类型。

  • 每个子类型必须存在,且与父类型在同一模块中
  • 每个子类型必须显式声明为 finalsealednon-sealed
  • 不允许运行时出现 permits 列表之外的子类,否则 switch 无法穷尽,编译失败

Switch 表达式要覆盖全部子类型

switch 的目标表达式类型要是密封父类型;每个 case 直接写子类名 + 变量绑定,Java 自动完成类型检查和字段解构。

  • case Circle c ->:c 是已强转且可直接使用的 Circle 实例
  • case Rectangle r ->:r 是 Rectangle 类型,可调用其方法或访问其字段
  • 所有 permits 列出的子类都必须出现在 case 中,少一个就编译报错

Null 必须单独处理

模式匹配 switch 不会忽略 null,也不默认跳到 default。即使输入理论上非空,也得显式写 case null。

  • case null -> throw new IllegalArgumentException("shape must not be null")
  • 也可以合并:case null, default -> ...,但 default 在密封类场景下通常冗余
  • 不写 null 分支,编译器直接拒绝,强制你正视空值边界

避免守卫条件破坏穷尽性认知

when 子句用于补充判断,但它不影响编译期穷尽性检查——只看类型是否全覆盖。

  • case String s when s.length() > 5 -> 和 case String s -> 同时存在,编译器仍认为 String 已覆盖
  • 不能靠 when 替代 case:比如只写 case String s when s.startsWith("A"),其他 String 就算漏掉
  • 复杂业务逻辑建议提前提取,保持 case 主干清晰、可验证

相关文章

精彩推荐