JEP 375: Pattern Matching for instanceof (Second Preview) | instanceof 的模式匹配(第二次预览版)
摘要
为 instanceof
操作符在 Java 编程语言中引入 模式匹配。模式匹配 允许程序中的常见逻辑,即从对象中条件性地提取组件,以更简洁和安全的方式表达。这是 JDK 15 中的一项 预览语言特性。
历史
instanceof
的模式匹配在 2017 年年中被 JEP 305 提出,并作为 JDK 14 中的 预览语言特性 在 2019 年底被定为目标。本 JEP 提议在 JDK 15 中重新预览此特性,与 JDK 14 中的预览相比没有更改,以收集更多反馈。
动机
几乎每个程序都包含某种逻辑,该逻辑将测试表达式是否具有某种类型或结构,然后条件性地提取其状态组件以进行进一步处理。例如,所有 Java 程序员都熟悉 instanceof-and-cast 模式:
if (obj instanceof String) {
String s = (String) obj;
// 使用 s
}
这里发生了三件事:一个测试(obj
是否是一个 String
?)、一个转换(将 obj
转换为 String
),以及一个新局部变量(s
)的声明,以便我们可以使用字符串值。这种模式简单直接,所有 Java 程序员都理解,但由于以下几个原因并不是最优的。这种方式很繁琐;同时进行类型测试和转换应该是没有必要的(instanceof
测试之后你还能做什么?)。这种样板代码——特别是 String
类型的三次出现——掩盖了后面更重要的逻辑。但最重要的是,这种重复为错误悄无声息地潜入程序提供了机会。
与其寻找临时解决方案,我们认为现在是 Java 拥抱 模式匹配 的时候了。模式匹配允许以简洁的方式表达所需对象的“形状”(即 模式),并允许各种语句和表达式将其输入与这种“形状”进行匹配(即 匹配)。从 Haskell 到 C#,许多语言都因模式匹配的简洁性和安全性而采用它。
说明
模式(pattern)是(1)可应用于目标的 谓词(predicate)和(2)一组 绑定变量(binding variables)的组合,这些绑定变量仅在谓词成功应用于目标时从目标中提取。
类型测试模式(type test pattern)由指定类型的谓词和一个绑定变量组成。
instanceof
操作符(JLS 15.20.2)现在可以接受类型测试模式而不仅仅是类型。在以下代码中,短语 String s
是类型测试模式:
if (obj instanceof String s) {
// 这里可以使用 s
} else {
// 这里不能使用 s
}
instanceof
操作符将目标 obj
与类型测试模式进行“匹配”,具体如下:如果 obj
是 String
的实例,则将其转换为 String
并赋给绑定变量 s
。绑定变量在 if
语句的真代码块中有效,而在 if
语句的假代码块中无效。
与局部变量不同,绑定变量的作用域由包含它的表达式和语句的语义确定。例如,在以下代码中:
if (!(obj instanceof String s)) {
.. s.contains(..) ..
} else {
.. s.contains(..) ..
}
真代码块中的 s
指的是封闭类中的一个字段,而假代码块中的 s
则是由 instanceof
操作符引入的绑定变量。
当 if
语句的条件变得比单个 instanceof
更复杂时,绑定变量的作用域也会相应地增长。例如,在以下代码中:
if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
绑定变量 s
在 &&
操作符的右侧以及真代码块中都是有效的。(右侧仅在 instanceof
成功并赋值给 s
后才会进行评估。)另一方面,在以下代码中:
if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}
绑定变量 s
在 ||
操作符的右侧以及真代码块中都不有效。(在这些点上的 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
的 语法 也相应地进行了扩展:
RelationalExpression:
...
RelationalExpression instanceof
ReferenceType
RelationalExpression instanceof
Pattern
Pattern:
ReferenceType Identifier
未来工作
未来的 JEP 将通过为其他语言构造(如 switch
表达式和语句)添加模式匹配来增强 Java 编程语言。
备选方案
类型测试模式的优势可以通过在 if
语句中使用 流类型(flow typing) 或通过 类型切换(type switch) 构造来实现。模式匹配是这两种构造的泛化。