JEP 305:instanceof
的模式匹配(预览版)
概述
动机
几乎每个程序都包含某种逻辑,用于测试表达式是否具有某种类型或结构,然后有条件地提取其状态的组成部分以进行进一步处理。例如,所有 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
与类型测试模式进行“匹配”:如果 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 程序中显式类型转换的总体数量。此外,类型测试模式在编写相等性方法时特别有用。请看以下从 《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
语句中的流类型或类型开关结构来获得。模式匹配概括了这两种结构。
依赖
该实现可能会使用 JEP 309(动态类文件常量)。