跳转至

instanceof 模式匹配#

版本信息

为什么需要#

JDK 8 里判断类型再使用,标准三件套是「instanceof 判断 + 显式强转 + 用强转后的变量」:

if (obj instanceof String) {
    String s = (String) obj;        // 又写一遍类型名,纯样板
    return s.length();
}

类型名写了两次、强转可有可无地写错、equals 判类型还要先 getClass()。JEP 394 让 instanceof 匹配成功即绑定一个模式变量——变量自动是目标类型,省去强转,作用域也精确到「确实匹配」的分支里。

语法#

examples/instanceof-pattern/真实可运行的源码(snippets 嵌入,与示例零漂移):

package com.javamodern.pminstance;

// JEP 394(JDK 16):instanceof 匹配即「绑定模式变量」,
// 消除 JDK 8 里「先 instanceof 判断、再强转使用」的样板。
public class PatternInstanceofDemo {

    public static String describe(Object obj) {
        if (obj instanceof String s) {                       // (1) 匹配则绑定 s,s 已是 String,无需强转
            return "字符串:" + s;
        }
        if (obj instanceof Integer i) {
            return "数字:" + i;
        }
        return "未知";                                        // null 或其它类型(null instanceof T 恒为 false)
    }

    public static void main(String[] args) {
        System.out.println(describe("hi"));   // 字符串:hi
        System.out.println(describe(42));     // 数字:42
        System.out.println(describe(null));   // 未知
    }
}
  1. :material-lightbulb: obj instanceof String s——匹配则 s 被绑定且已是 String,无需强转;分支内直接用 s

运行 PatternInstanceofDemo.main 的实测输出(JDK 17):

字符串:hi
数字:42
未知

与 JDK 8 旧写法对比#

if (obj instanceof String) {
    String s = (String) obj;        // 显式强转,类型名写两遍
    return "字符串:" + s;
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    return "数字:" + i;
}
return "未知";
if (obj instanceof String s) {       // 匹配即绑定,s 已是 String
    return "字符串:" + s;
} else if (obj instanceof Integer i) {
    return "数字:" + i;
}
return "未知";

底层原理#

instanceof T x类型模式(type pattern):运行时若对象确实是 T,就把对象绑定到变量 x(其静态类型即 T),分支内编译器已知 x: T,故无需强转。关键是作用域的精确性(flow typing)x 只在「编译器能证明 instanceof 成立」的范围内可用——if 的 then 分支、&& 的右侧、! 取反后的 else 分支等;null instanceof T x 恒为 false,所以 x 不会绑定到 null

graph LR
    C["obj instanceof String s"] --> M{"运行时 obj 是 String?"}
    M -- 是 --> B["绑定 s(类型 String)<br/>分支内直接用,无需强转"]
    M -- 否 --> N["s 不在作用域"]
    M -.obj 为 null.- N
    style C fill:#bbdefb
    style B fill:#c8e6c9

常见坑 / 最佳实践#

  • null 永不匹配null instanceof String sfalses 不会被绑定;这点和旧 instanceof 一致。
  • 作用域随控制流收缩if (!(obj instanceof String s)) return; 之后,s 也可用(取反后往下即「确实匹配」)。但跨方法、跨赋值后作用域可能消失,编译器会提示。
  • switch 模式匹配(JDK 21)配套:分支多了用 instanceof 串联会变长,JDK 21 的 switch 模式匹配 是它的自然演进。
  • 不要为了用而拆:如果分支只有一处类型判断,instanceof 模式变量已足够;多分支再上 switch 模式匹配。

小结#

instanceof 模式匹配消除「判断 + 强转」样板,让类型分支更安全、更短。它是 JDK 17 语言特性里最日常、收益最直接的一项,也是后续 record 解构、switch 模式匹配的基石。

参考#