Java 17 switch模式匹配支持类型直接匹配、null处理、密封类穷尽检查和受限变量作用域,提升可读性与安全性,但需避免字符串匹配误用、子类顺序错误及作用域越界等问题。

switch 表达式中直接匹配类型,省掉 instanceof + if 嵌套
Java 17 的 switch 模式匹配允许你在 case 标签里直接写类型名,并自动绑定变量,不再需要先用 instanceof 判断、再强转、再进 if 块。这是最直观的可读性提升点。
常见错误现象:有人把 switch (obj) 写成 switch (obj.getClass().getSimpleName()),结果触发字符串匹配逻辑,完全绕开模式匹配能力,编译器也不报错——只是没生效。
- 必须确保
obj是引用类型(Integer、String、自定义类等),原始类型如int不支持类型模式 -
case null ->是合法语法,显式处理空值;不写则遇到null会抛NullPointerException - 多个
case的顺序有影响:子类case必须放在父类case前面,否则编译失败(例如case Integer ->要在case Number ->之前)
配合 sealed 类实现穷尽性检查,避免漏分支
当 switch 的选择器是密封类(sealed)或其子类型时,JDK 17+ 编译器能静态验证是否覆盖了所有允许的子类。这是可读性背后的可靠性支撑——你一眼就能看出“该写的都写了”,不用靠注释或测试去猜。
使用场景:领域模型中固定几种状态(如 OrderStatus)、消息协议中的几种事件类型(如 Event 子类)、图形渲染中的形状(Circle / Square / Triangle)。
- 若漏写某个
permits列出的子类,编译时报错:the switch expression does not cover all possible input values - 不能用
default来“兜底”绕过检查;只有所有子类都显式列出,才认为穷尽 - 如果某子类是
non-sealed,那它自身可能还有未限定的子类,此时编译器无法穷尽,会退回到要求写default
模式变量作用域天然受限,减少误用风险
在 case Type varName -> 中声明的 varName,只在该箭头右侧表达式或代码块内有效。这和老式 if (obj instanceof Type) { Type t = (Type) obj; ... } 中手动声明的变量相比,天然规避了“在条件外误用转换后变量”的问题。
容易踩的坑:有人试图在 switch 外部访问某个 case 绑定的变量,比如:
Object obj = new String("hello");
String s;
switch (obj) {
case String s -> System.out.println(s.length());
case Integer i -> s = i.toString(); // ❌ 编译错误:s 已被声明为模式变量,不可重复赋值
}
System.out.println(s); // ❌ 编译错误:s 不在作用域内
- 每个
case的模式变量相互独立,名字可以重复(case String s ->和case Integer s ->合法) - 箭头右侧如果是块语句(
{ ... }),变量作用域仅限于该块;单表达式则限于该表达式 - 不支持在
default分支中声明模式变量(因为无类型可推)
避免 fallback 逻辑,强制显式处理每种类型
传统 switch 依赖 break,漏写就导致意外 fall-through;而模式匹配 switch 使用 -> 语法,默认不穿透,且作为表达式时必须有返回值(或抛异常)。这对多态分支是天然约束——你没法偷偷跳过某种类型的处理逻辑。
性能影响很小:底层仍是类型检查 + 分派,JIT 优化后与手工 if-else 差异可忽略;但可读性提升是即时的。
- 作为语句使用时(无返回值),每个
case右侧可以是表达式、块或throw - 作为表达式使用时(
String result = switch (obj) { ... };),必须覆盖所有可能值(或含default),否则编译失败 - 不要为了“统一返回”而在每个
case里重复写return;用表达式形式更干净
真正难的是设计好密封类层次——模式匹配再优雅,也救不了随意开放继承、子类爆炸的模型。类型边界越清晰,switch 越简洁可靠。


















