Skip to content

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 模式:

java
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 是类型测试模式:

java
if (obj instanceof String s) {
    // 这里可以使用 s
} else {
    // 这里不能使用 s
}

instanceof 操作符将目标 obj 与类型测试模式进行“匹配”,具体如下:如果 objString 的实例,则将其转换为 String 并赋给绑定变量 s。绑定变量在 if 语句的真代码块中有效,而在 if 语句的假代码块中无效。

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

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

真代码块中的 s 指的是封闭类中的一个字段,而假代码块中的 s 则是由 instanceof 操作符引入的绑定变量。

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

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

绑定变量 s&& 操作符的右侧以及真代码块中都是有效的。(右侧仅在 instanceof 成功并赋值给 s 后才会进行评估。)另一方面,在以下代码中:

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

绑定变量 s|| 操作符的右侧以及真代码块中都不有效。(在这些点上的 s 指的是封闭类中的一个字段。)

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

instanceof 中使用模式匹配应该能够显著减少 Java 程序中显式类型转换的总数。此外,类型测试模式在编写相等性方法时特别有用。考虑以下来自 Effective Java 书籍 的第 10 条项的相等性方法:

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

使用类型测试模式,该方法可以重写为更清晰的形式:

java
@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) 构造来实现。模式匹配是这两种构造的泛化。