跳转到内容

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

原文:https://openjdk.org/jeps/375
翻译:张欢

instanceof运算符的模式匹配来增强Java编程语言。模式匹配使程序中的通用逻辑,即从对象中有条件地提取组件,得以更简洁、更安全地表示。这是JDK 15的一个预览语言特性

历史

instanceof的模式匹配在2017年中期由JEP 305提出,并在2019年末在JDK 14中作为预览语言特性。本JEP提出在JDK 15中再次预览该特性,相对于JDK 14中的预览没有变化,以便收集附加反馈。

动机

几乎每个程序都包含某种逻辑,结合了对表达式的类型或结构的测试,然后有条件地提取其中的状态组件以进行进一步处理。例如,所有Java程序员都熟悉“先instanceof再转换”的习惯用法:

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

这里发生了三件事情:一个测试(obj是不是一个String),一个转换(将obj转换为String),和定义一个新的局部变量(s)以便我们可以使用字符串的值。这种模式很简单,并且所有Java程序员都可以理解,但是由于一些原因,这不是最优的。这很乏味:应该没有必要既做类型测试,同时又做类型转换(你还能在instanceof测试之后做什么其他的呢?)。这些样板代码——特别是出现了三次的String类型——混淆了后面更重要的逻辑。但最重要的是,重复代码为错误提供了机会且不易被察觉。

与寻求特定的解决方案相比,我们相信是时候让Java拥抱模式匹配了。模式匹配允许简洁地表达对象所需的“形态”(模式),并允许各种语句和表达式针对其输入来测试“形态”(匹配)。从Hashkell到C#,许多语言都出于其简洁性和安全性而拥抱了模式匹配。

描述

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

类型测试模式由指定类型的谓词和单个绑定变量组合。

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

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

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引用封闭类型中的字段。)

当目标是nullinstanceof的工作方式不变。即,只有obj不是null时,模式才会匹配,s才会被赋值。

instanceof中使用模式匹配应该会大大减少Java中显式强制转换的总数。此外,类型测试模式在编写equals方法时特别有用。考虑下面选择《Effective Java》书中第10条的equals方法:

@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将通过与其他语言结构,如switch表达式和语句,进行模式匹配来增强Java编程语言。

备选方案

类型测试模式的好处可以通过if语句中的的流程类型类型switch结构中获得。模式匹配涵盖了这两种结构。

依赖

实现可能用到JEP 309(动态Class文件常量)