JDK JDK 学习手册8 → 27 · JDK 专用学习站
QUICKREF · LANGUAGE · JEP 394

instanceof 模式:判断与绑定合并

JDK 17 · 速查主题 03 / 06 · 预计 12 分钟
基础12 分钟JEP 394
一句话instanceof 模式把类型判断和变量声明合并,省掉强转。它的关键是流作用域:变量只在编译器能证明匹配成功的路径上可见,而不是简单的块作用域。

学完你要能自己回答

  1. 模式变量为什么能在 && 右侧使用,在 || 右侧却不行?
  2. 提前 return / throw 为什么能让后续代码使用模式变量?
  3. 17 的 instanceof 模式与 21 的 record pattern 差在哪?

判断成功即绑定01

o instanceof String s 在判断类型的同时声明并绑定 s,匹配成功后 s 就是 String,不需要再强转。匹配失败时 s 不会被赋值。

static String normalize(Object o) {
    if (o instanceof String s && !s.isBlank()) {
        return s.strip().toLowerCase();
    }
    return "";
}
System.out.println("[" + normalize("  Hello  ") + "]");
System.out.println(normalize("   ").isEmpty());
[hello]
true

旧写法需要三步:instanceof 判断、强制转换、再访问。模式把三步压成一条声明,也消除了「判断错类型后强转」的机会。

模式变量不是 final,但不要在匹配后的分支里重新赋值;那会让读者误以为它仍代表原始匹配值。

流作用域:由控制流决定可见性02

模式变量的作用域由流分析决定,而不是声明位置所在的块。编译器只允许在「确定匹配成功」的路径上访问它。

写法右侧/后续能否使用 s原因
o instanceof String s && s.length() > 0可以&& 右侧只在左侧为 true 时求值
o instanceof String s || s.length() > 0不可以左侧为 false 时右侧仍会求值,s 未绑定
if (!(o instanceof String s)) return; 之后可以提前返回后,后续路径必然匹配成功
if (o instanceof String s) { ... } 块外不可以块外可能未匹配
static String upper(Object o) {
    if (!(o instanceof String s)) {
        return "not a string";
    }
    return s.toUpperCase();
}

System.out.println(upper("ok"));
System.out.println(upper(42));
OK
not a string

同理,throw、break、continue 让后续路径不可达时,编译器也会承认模式变量已绑定。这不是简单的「if 块内可见」,而是对布尔短路和控制流的完整分析。

不要把流作用域当成块作用域。if (o instanceof String s) { ... } 之外访问 s 一定编译失败,即使你觉得「逻辑上已经判断过了」。

限制与边界03

  • 不能对泛型参数做具体类型判断:o instanceof List<String> l 非法,只能写 List<?> l。
  • null instanceof 任何类型 都是 false,模式变量不会绑定。
  • 17 只支持类型模式,不支持 record 解构;o instanceof Point(int x, int y) 要等到 21。
  • switch 里的类型模式在 17 是预览,instanceof 模式本身是正式能力,两者不要混为一谈。
  • 模式变量可以与已有变量同名吗?不可以,同一作用域内重复声明会编译失败。
// 合法:泛型用通配符
if (o instanceof List<?> list && !list.isEmpty()) {
    System.out.println(list.get(0));
}

// 非法:泛型参数不可具体化
// if (o instanceof List<String> list) { ... }

类型擦除决定了运行时没有 List<String> 的信息,编译器无法验证元素类型,因此禁止这种写法。需要元素类型时,只能绑定 List<?> 后逐个判断。

instanceof 模式解决的是「类型分支 + 强转」的噪音,不解决「数据形状解构」。后者是 record pattern 的职责,而它在 17 尚不存在。

什么时候用、什么时候不用04

场景推荐写法说明
单一类型判断 + 使用if (o instanceof String s)最直接,作用域清晰
多类型分支sealed + 预览 switch(17)类型模式 switch 仍是预览,生产代码谨慎
开放类型多分支if / else if 链没有封闭集合,switch 无法穷尽
判断后只调用一个方法模式 + 直接调用避免为了强转多写一行
需要解构字段21 的 record pattern17 只能强转后调访问器

instanceof 模式最适合「一个方法里处理一两种已知类型」的场景。分支超过三四种,且类型集合封闭时,才考虑 sealed + 模式 switch;否则 if 链更易读。

不要用 instanceof 模式替代多态。如果每个类型都有统一行为,把行为放进接口方法,而不是在调用方按类型分支。

版本边界BOUNDARY

这张表比 API 本身更值得记。写代码时先确认你的运行时版本。

特性 / API状态说明
instanceof 模式16 正式JEP 394;JDK 14 / 15 预览。
流作用域16 正式由编译器流分析决定,不是块作用域。
泛型类型模式不允许类型擦除导致无法验证,只能使用通配符。
record pattern21 正式17 不支持解构 record。
switch 类型模式17 预览JEP 406;需要 --enable-preview。

费曼自检SELF CHECK

先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。

01. 为什么 o instanceof String s || s.length() > 0 编译失败?
|| 右侧在左侧为 false 时仍会求值,此时 s 没有绑定。编译器只在确定匹配成功的路径上允许访问模式变量,所以拒绝这段代码。
02. 提前 return 为什么能让后面的代码使用 s?
if (!(o instanceof String s)) return; 之后,能继续执行的路径必然满足匹配条件,流分析因此认为 s 已绑定。throw、break、continue 也有类似效果。
03. instanceof 模式能判断 List<String> 吗?
不能。泛型参数在运行时被擦除,编译器无法验证元素类型,只允许 List<?> 这样的通配符类型。
04. instanceof 模式与 switch 类型模式是什么关系?
两者共享类型模式的概念,但 instanceof 模式在 16 已正式,switch 类型模式在 17 仍是预览。17 的 switch 类型模式还需要 && 守卫,21 才改为 when 并正式。
学习进度 记录保存在本机浏览器 · 不上传

JDK 学习手册 · JDK / Java 专用学习站 · 纯静态 · 双击 index.html 即可使用

本站专注 JDK 版本演进与 Java 教学,非 Oracle / OpenJDK 官方站点。版本数据来自 OpenJDK 官方 Features 表;课程示例已在对应本机 JDK 实测。构建日期 2026-11-12。