bigywater.tech
Java · 277

Switch 守卫条件与复杂模式

守卫(when)、嵌套 record 模式、泛型 sealed 与枚举混合,让 switch 的规则变得复杂。哪些写法会被编译器拒绝?运行期的求值顺序和异常怎么表现?这篇用大量真实编译与运行逐条验证。

when
守卫的求值:按顺序,命中即停
89 vs 34 ms
1000 万次六种类型分派:模式 switch / instanceof 链
javac 漏检
守卫里的受检异常:javac 21 与 27 都编译通过

守卫与绑定变量

实测 · 本机执行输出
  正常:case A a when a.x() > 0 -> 1; case A a -> 2; case B b -> 3 → 编译通过
  只有带守卫的 A,没有无守卫的 A(不穷尽)                                     → the switch expression does not cover all possible input values
  无守卫的 A 在带守卫的 A 前面(被支配)                                     → this case label is dominated by a preceding case label
  守卫恒为 true(when true)后面再来无守卫的同类型                            → this case label is dominated by a preceding case label
  守卫里用绑定变量与 &&(s.length() 依赖 instanceof 绑定)                  → 编译通过
  守卫里用 ||(绑定变量不一定存在)                                         → 编译通过
  守卫里的绑定变量用在 || 的右边                                          → cannot find symbol
  守卫里修改绑定变量(s = "x")                                         → cannot assign to s, as it was not declared inside the guard
  守卫表达式不是 boolean(when 1)                                    → incompatible types: int cannot be converted to boolean
  守卫里调用抛受检异常的方法(没有 throws)                                   → 编译通过
  同一个 case 里用逗号写两个有绑定变量的模式                                   → illegal fall-through from a pattern
  case null, String s(null 与有绑定的模式合并)                        → invalid case label combination

嵌套 record 模式与泛型

实测 · 本机执行输出
  Opt<T> 的 Some / None,带泛型的穷尽 switch                         → 编译通过
  Opt<String> 上用 Some<Integer> 模式(类型参数不兼容)                   → incompatible types: Opt<String> cannot be converted to Some<Integer>
  Pair<Box<A>, Box<B>> 的嵌套 record 模式,穷尽                      → 编译通过
  嵌套模式缺一个组合(Box(A) 与 Box(A) 的组合没有覆盖 Box(B))                  → the switch expression does not cover all possible input values
  record 模式的组件个数不对(Box(var a, var b))                        → incorrect number of nested patterns
  record 模式里同名绑定变量重复(Pair(var x, var x))                     → illegal attempt to redefine an existing match binding
  record 模式匹配 Box<S>(null 组件不匹配类型模式 A)                       → 编译通过
  record 模式用 var 加守卫  case Box(var v) when v != null         → 编译通过

枚举与 sealed 混合

实测 · 本机执行输出
  sealed Shape2 的成员有枚举 Color:用 case Color.RED、case Color.GREEN、case Sq s → 编译通过
  同上,缺少 Color.GREEN(不穷尽)                                     → the switch expression does not cover all possible input values
  Color 常量在 Color 类型的 switch 里写成 case Color c when …         → 编译通过
  case RED(不限定)在 Shape2 的 switch 里                           → cannot find symbol
  常量标签 case 1 在 Integer 模式 case Integer i 之后                 → this case label is dominated by a preceding case label
  常量标签 case 1 在 case Integer i when … 之前                     → 编译通过
  字符串常量与 String 模式                                           → 编译通过

运行期行为

实测 · 本机执行输出
  三个 A 分支的守卫依次求值:结果 mid,守卫调用 2 次(命中第二个后停止)
  守卫里抛出的异常直接传播(不是 MatchException):StringIndexOutOfBoundsException: Index 10 out of bounds for length 4
  没有 case null 时 switch(null) → NullPointerException
  case null -> 与 default 分开写:null 分支
  Box(null) 对 case Box(A) 与 case Box(B)(没有全能分支)→ MatchException(null 组件不匹配类型模式,穷尽性要求兜底)
  Box(null) 加上 case Box<S>(var v) 全能分支:全能分支(v=null)
  嵌套模式 Pair(A, B) 解构:A.x=1,B.s=z
  守卫里重新引用 instanceof 绑定:偶数大于 10:42
  1000 万次分派(6 种类型):模式 switch 89 ms;instanceof 链 34 ms(结果相同 true)
  JDK 21:守卫里调用 throws Exception 的方法,而 f 没有声明 throws:javac 编译通过;捕获到 java.lang.Exception: checked-from-guard
  JDK 27:守卫里调用 throws Exception 的方法,而 f 没有声明 throws:javac 编译通过;捕获到 java.lang.Exception: checked-from-guard

分步演示

Switch 守卫与复杂模式
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12(模式 switch 正式特性)与 27(只用于核对受检异常的现象)
    • 没有验证:JDK 22 之后原始类型模式与未命名模式对本篇规则的扩展(见后面的篇目)、模式 switch 的 invokedynamic 实现细节(见前面的篇目)、守卫里受检异常是否是已知缺陷、带泛型 record 的类型推断在复杂嵌套下的所有边界、模式 switch 与 instanceof 链的性能差异的原因
    京ICP备2026067502号-1京公网安备11010502063487号