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与类型测试模式相“匹配”:如果obj是String的实例,那就将其转换为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中显式强制转换的总数。此外,类型测试模式在编写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文件常量)。