跳到主要内容

JEP 375:instanceof 的模式匹配(第二预览版)

QWen Max 中英对照 JEP 375: Pattern Matching for instanceof (Second Preview)

总结

通过为 instanceof 操作符添加模式匹配来增强 Java 编程语言。模式匹配 允许程序中的常见逻辑,即从对象中条件性地提取组件,能够以更简洁和安全的方式表达。这是 JDK 15 中的预览语言功能

历史

instanceof 的模式匹配由 JEP 305 于 2017 年年中提出,并于 2019 年年底作为 预览语言特性 定位到 JDK 14。本 JEP 建议在 JDK 15 中重新预览该功能,与 JDK 14 中的预览相比没有任何变化,以收集更多反馈。

动机

几乎每个程序都包含某种逻辑,用于测试表达式是否具有某种类型或结构,然后有条件地提取其状态的组成部分以进行进一步处理。例如,所有 Java 程序员都熟悉 instanceof-and-cast 模式:

if (obj instanceof String) {
String s = (String) obj;
// use s
}

这里有三件事情正在进行:一个测试(obj 是不是一个 String?),一个转换(将 obj 转换为 String),以及声明一个新的局部变量(s),这样我们就可以使用字符串值。这种模式很简单,所有 Java 程序员都能理解,但由于几个原因,它并不是最优的。它很繁琐;进行类型测试和转换应该是不必要的(在 instanceof 测试之后你还能做些什么呢?)。这种样板代码——特别是 String 类型的三次出现——掩盖了后续更重要的逻辑。但最重要的是,重复提供了让错误悄无声息地潜入程序的机会。

与其寻求特定的解决方案,我们认为现在是 Java 拥抱模式匹配的时候了。模式匹配允许对象所需的“形状”被简洁地表达(即模式),并且各种语句和表达式可以针对其输入测试该“形状”(即匹配)。从 Haskell 到 C#,许多语言都因为其简洁性和安全性而采用了模式匹配。

描述

模式 是以下两者的组合:(1) 可应用于目标的 谓词,以及 (2) 仅在谓词成功应用于目标时从目标中提取的一组 绑定变量

一个 类型测试模式 包括一个指定类型的谓词,以及一个单独的绑定变量。

instanceof 运算符(JLS 15.20.2)被扩展为接受类型测试模式,而不仅仅是类型。在下面的代码中,短语 String s 就是类型测试模式:

if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}

instanceof 运算符按如下方式将目标 obj 与类型测试模式进行“匹配”:如果 objString 的一个实例,那么它会被强制转换为 String 并赋值给绑定变量 s。该绑定变量在 if 语句的 true 块中是有效的,而在 if 语句的 false 块中则无效。

绑定变量的作用域与局部变量的作用域不同,它由包含表达式和语句的语义决定。例如,在以下代码中:

if (!(obj instanceof String s)) {
.. s.contains(..) ..
} else {
.. s.contains(..) ..
}

true 块中的 s 指的是封闭类中的一个字段,而 false 块中的 s 指的是由 instanceof 运算符引入的绑定变量。

if 语句的条件比单个 instanceof 更复杂时,绑定变量的作用域会相应扩大。例如,在以下代码中:

if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}

绑定变量 s&& 运算符的右侧以及 true 块中都处于作用域内。(只有在 instanceof 成功并赋值给 s 时,才会评估右侧表达式。)另一方面,在以下代码中:

if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}

绑定变量 s 在 || 运算符的右侧不在作用域内,也不在 true 块的作用域内。(此时的 s 指的是封闭类中的一个字段。)

当目标为 null 时,instanceof 的工作方式没有变化。也就是说,只有当 obj 不为 null 时,模式才会匹配,并且会为 s 赋值。

instanceof 中使用模式匹配应该能够显著减少 Java 程序中显式类型转换的总体数量。此外,类型测试模式在编写相等性方法时特别有用。请看以下从 《Effective Java》 第 10 条中摘录的相等性方法:

@Override public boolean equals(Object o) { 
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

使用类型测试模式意味着它可以改写为更清晰的:

@Override public boolean equals(Object o) { 
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}

instanceof 语法 也相应地进行了扩展:

关系表达式:
     ...
     关系表达式 instanceof 引用类型
     关系表达式 instanceof 模式

模式:
     引用类型 标识符

未来工作

未来的 JEP 将通过模式匹配来增强 Java 编程语言的其他语言结构,例如 switch 表达式和语句。

替代方案

类型测试模式的好处可以通过 if 语句中的流类型类型切换结构来获得。模式匹配概括了这两种结构。