java 17+的模式匹配switch本身不提供编译期完备性检查,但配合正确声明的密封类(sealed)和类型模式(如case result.success s:),可在启用预览特性(java 17–20)或java 21+标准支持下实现子类型穷举检查。

新版 switch(Java 17+ 的模式匹配 switch)本身**不直接提供编译期完备性检查**,但配合密封类(sealed),可以逼近“穷举所有子类型”的效果——前提是正确声明、正确使用、且启用相应编译器支持。
密封类必须显式列出所有直接子类
这是整个机制成立的前提。如果漏写某个子类,或子类未用 permits 声明,编译器会报错;但更常见的是:子类被定义在另一个模块/包中却未被 permits 引用,导致 switch 仍视为“非封闭”,从而退化为普通 switch 行为(无穷举检查)。
实操建议:
-
sealed类必须用permits明确列出所有**直接**子类(不能是间接继承链中的类) - 所有被允许的子类必须是
final、sealed或non-sealed—— 若选non-sealed,则后续子类可自由扩展,此时switch就无法保证穷举 - 若子类与密封父类不在同一文件,确保它们在同一个模块(或默认模块)下,且子类名拼写完全一致(含包路径)
switch 必须使用类型模式(type pattern),不能只用常量 case
写成 case Integer i: 或 case Result.Success s: 才触发模式匹配和穷举检查;如果写成 case SUCCESS:(枚举常量)或 case null:(守卫条件),编译器不会将其纳入“覆盖所有密封子类”的验证逻辑。
常见错误现象:
- 编译通过,但 IDE 提示 “Switch expression does not cover all possible values” —— 实际是用了老式
case写法,没启用模式匹配 - 误把
instanceof判断逻辑直接搬进switch,例如case var x when x instanceof Error e:,这绕过了类型模式,也失去穷举保障
正确写法示例:
return switch (result) {
case Result.Success s -> s.value();
case Result.Failure f -> throw new RuntimeException(f.message());
// 编译器会强制你补全所有 sealed 子类,否则报错
};
必须开启 --enable-preview(Java 17~20)或无需预览(Java 21+)
Java 17 和 18 中,模式匹配 switch 是预览特性,需显式启用;Java 19 是第二次预览;Java 21 起才转正。若用旧 JDK 或未加参数,即使语法写对,也会编译失败。
使用场景与参数差异:
- Java 17–20:编译时加
--enable-preview,运行时也必须加,否则UnsupportedOperationException或IncompatibleClassChangeError - Java 21+:无需任何 flag,但需确认
javac -version输出确实是 21 或更高 - Maven 用户注意:
maven-compiler-plugin的source/target设为21不够,还得设release或显式传compilerArgs
IDE 和构建工具可能缓存旧语义,导致“明明写对了却不报错/不提示”
IntelliJ 默认可能未启用 preview 特性,或 Maven 导入后未刷新语言级别;Gradle 的 java.toolchain 若指向 JDK 17 但没设 enablePreview = true,也会静默失效。
容易踩的坑:
- 代码在命令行能编译报错(提示缺 case),但在 IDEA 里没红线、也没警告 —— 检查 Settings → Build → Compiler → Java Compiler → “Use compiler from module SDK” 是否勾选,以及是否设置了正确的 language level
- Gradle 构建成功,但 CI 失败 —— 很可能是 CI 使用的 JDK 版本低于 21,且未配
--enable-preview - 写了所有子类 case,但仍有编译错误提示 “missing default clause” —— 这说明编译器仍未识别该类型为密封类,回头检查
permits和子类修饰符
真正起作用的不是 switch 本身有多智能,而是密封类 + 类型模式 + 编译器三者对齐后的约束传递。一旦其中一环松动(比如子类被意外设为 non-sealed,或 switch 里混用了表达式模式),穷举保障就消失了,而且往往不报错,只是默默失效。










