← 返回题目列表

Java 模式匹配如何简化 instanceof 和 switch?

高频 困难 第 16 / 24 题 更新于 2026/07/26
模式匹配instanceofswitchJava 21

简化版

instanceof 模式匹配在 Java 16 正式发布,把类型检查和安全转换合并,并根据控制流判断模式变量作用域;switch 模式匹配在 Java 21 正式发布,可直接按类型、守卫条件和 null 分支匹配。编写 switch 时还必须满足分支不被前面更宽的模式支配,并对可能输入保持穷尽。

详细版

if (obj instanceof String s) 只有匹配成功时才引入 snull 不会匹配类型模式。编译器做流敏感作用域分析,因此在 && 右侧可以使用变量,在可能未匹配的 || 右侧则不能使用。

模式 switch 按从上到下的首个适用分支执行,具体类型应写在宽泛类型之前;Java 21 使用 when 添加守卫。传统 switch 遇到 null 通常抛 NullPointerException,模式 switch 可以显式声明 case null,但不会因为支持模式就默认接纳 null。

完整版教学

一、instanceof 模式变量

传统写法要先检查再强制转换,类型名重复且容易转错:

if (value instanceof String) {
    String text = (String) value;
    System.out.println(text.length());
}

模式匹配将测试、转换和变量声明合为一体:

if (value instanceof String text && !text.isBlank()) {
    System.out.println(text.length());
}

只有左侧模式成功,&& 右侧才执行,所以 text 在那里肯定已赋值。写成 value instanceof String text || text.isBlank() 则不能编译,因为进入右侧时可能正是模式失败的情况。

二、流敏感作用域

模式变量不是简单地从声明位置一直存活到代码块末尾,编译器会根据控制流判断它是否必然匹配:

if (!(value instanceof String text)) {
    return;
}
System.out.println(text.length());

执行到打印语句意味着失败分支已经返回,因此 text 必然存在。理解这点比死记某一种 if 写法更可靠。类型模式不会匹配 null,所以上述成功路径中的 text 也不会是 null。

三、switch 类型模式与守卫

static String format(Object value) {
    return switch (value) {
        case null -> "空值";
        case Integer number when number > 0 -> "正整数";
        case Integer number -> "其他整数:" + number;
        case String text -> "文本:" + text;
        default -> "其他类型";
    };
}

守卫使用 when,只在类型模式匹配后判断附加条件。带守卫的正整数分支必须放在普通 Integer 分支之前,否则前面的宽泛分支会先接住所有 Integer。

四、支配关系与穷尽性

模式 switch 从上到下选择首个适用标签,所以 case Object object 若放在 case String text 前,会让 String 分支永远不可达,编译器将这种情况判为支配错误。这与传统 switch 中重复 case 的错误目的相同:阻止无意义分支。

switch 表达式必须覆盖所有可能值。选择器是普通开放类型时,通常需要 default;选择器是 sealed 类型时,列出所有允许分支后可以不写 default,编译器能基于密封层次检查穷尽性。显式 case null 与类型穷尽性是两个维度,因为类型模式本身不匹配 null。

五、Record Pattern 的配合

Java 21 还正式加入 Record Pattern,可以直接解构 Record 组件,并与类型模式嵌套:

record Point(int x, int y) {}

static int distanceSquared(Object value) {
    return switch (value) {
        case Point(int x, int y) -> x * x + y * y;
        default -> 0;
    };
}

这适合读取透明数据载体,但不应把一个复杂 switch 堆满业务逻辑。模式匹配改善的是类型检查和数据提取,领域多态更自然时,仍可把行为放回各类型的方法中。

六、版本边界

instanceof 模式匹配由 JEP 394 在 Java 16 定稿;switch 模式匹配由 JEP 441、Record Pattern 由 JEP 440 在 Java 21 定稿。旧版本曾使用预览语法,尤其守卫语法经历过变化,面试和项目代码应先确认目标 JDK,避免照搬早期预览示例。

七、用分支顺序推演支配关系

假设选择器声明类型为 Object,运行值是字符串。case CharSequence cs 已经覆盖所有 String,因此若后面再写 case String s,后者永远不可达;把 String 放前、CharSequence 放后才有意义。守卫不是任意常量 case 的通用 if,Java 21 的 when 只跟在模式标签之后。

前置 case后置 case是否可达原因
String sObject oObject 还能接其他类型
Object oString sString 已被 Object 支配
Integer i when i > 0Integer i后者接剩余整数
Integer iInteger i when i > 0所有 Integer 已被接住

一个 sealed 层次有 3 个允许分支时,switch 列出 3 个具体类型即可通过穷尽检查;新增第 4 个允许分支后,旧源码重新编译会被提示补分支。运行时若类型层次与编译时不一致,所谓穷尽也可能以 MatchException 失败,因此部署时仍要保证类版本一致。

记忆钩子:模式 switch 按“从窄到宽”排列;类型覆盖负责穷尽,case null 单独负责空值,二者不要混为一谈。

八、常见误区与追问

  • 误区:instanceof String s 在 value 为 null 时也会得到 s=null。 类型模式对 null 不匹配,成功路径中的模式变量为非 null。
  • 误区:模式变量的作用域只由大括号决定。 编译器做流敏感分析,提前 return 后变量也可能在后续路径可用。
  • 误区:模式 switch 会自动接受 null。 未显式 case null 时仍可能抛 NullPointerException,类型模式不会接住 null。
  • 误区:宽类型 case 放前面只是风格不好。 它会支配后续窄类型,编译器将不可达分支判错。
  • 追问:switch pattern statement 也必须穷尽吗? Java 21 中使用模式或 null 标签等增强形式的 switch statement 也受穷尽性检查。
  • 追问:Record Pattern 何时正式发布? JEP 440 与 switch 模式 JEP 441 都在 Java 21 定稿,不应混用旧预览守卫语法。
  • 追问:sealed 层次为什么有助于穷尽检查? 编译器知道许可的直接子类型集合,可以验证所有可达分支是否覆盖。

九、加强记忆

instanceof 模式把“判断、转换、声明”合成一步,变量是否可用由控制流决定。switch 模式按顺序选择首个匹配,具体分支放前、宽泛分支放后,守卫用 when;同时记住类型模式不吃 null,分支还要满足支配与穷尽规则。