红帽白皮书新鲜出炉!点击获取,让你的云战略更胜一筹! 了解详情
写点什么

与 Brian Goetz 聊 Java 的模式匹配

  • 2017-10-08
  • 本文字数:3905 字

    阅读完需:约 13 分钟

InfoQ 采访了来自 Oracle 的 Java 语言架构师 Brian Goetz 和编程语言研究员 Gavin Bierman ,谈论了有可能被集成到 Java 语言中的模式匹配

动机

之所有要研究是否有可能在 Java 中加入模式匹配,主要还是为了改进 Java 的语言特性。假如有这样的一段代码:

复制代码
if (obj instanceof Integer) {
int intValue = ((Integer) obj).intValue();
// 使用 intValue
}

这段代码做了三个操作:

  • 判断 obj 是否是一个 Integer 类型
  • 将 obj 转成 Integer 类型
  • 从 Integer 中抽取出 int

现在再来看看在 if…else 结构的语句中判断其他类型。

复制代码
String formatted = "unknown";
if (obj instanceof Integer) {
int i = (Integer) obj;
formatted = String.format("int %d", i);
}
else if (obj instanceof Byte) {
byte b = (Byte) obj;
formatted = String.format("byte %d", b);
}
else if (obj instanceof Long) {
long l = (Long) obj;
formatted = String.format("long %d", l);
}
else if (obj instanceof Double) {
double d = (Double) obj;
formatted = String.format("double %f", d);
}
else if (obj instanceof String) {
String s = (String) obj;
formatted = String.format("String %s", s);
}
...

虽然上述的代码是可运行的,也很好理解,但写起来很枯燥(太多重复的样板代码),也容易产生 bug。过多的样板代码会让业务逻辑变得含糊不清——如果 instanceof 方法已经判断出传入的实例是何种类型,那么就没必要重复进行转型了。

Goetz 和 Bierman 解释了他们想要做出的改进。

我们认为是时候让 Java 拥抱模式匹配了,而不仅仅是把它作为一种临时的解决方案。很多编程语言从 60 年代开始就已经采用了模式匹配,包括面向文本的编程语言 SNOBOL4 AWK 、函数编程语言 Haskell 和 ML,以及最近的面向对象编程语言 Scala 和 C#。

模式由判断谓语(predicate)和一系列绑定变量(binding variable)组成,判断谓语被应用在目标上面,而绑定变量是从目标中抽取出来的。

复制代码
if (x matches Integer i) {
// 使用 i
}

Goetz 和 Bierman 使用过各种模式匹配,它们使用了关键字 matches 和 exprswitch。

matches 操作符

matches 操作符可以用来代替 instanceof 操作。例如:

复制代码
String formatted;switch (obj) {
case Integer i: formatted = String.format("int %d", i); break;
case Byte b: formatted = String.format("byte %d", b); break;
case Long l: formatted = String.format("long %d", l); break;
case Double d: formatted = String.format(“double %f", d); break;
default: formatted = String.format("String %s", s);
}...
{1}

只有当变量 x 与某个 Integer 实例匹配时,变量 i 才能被使用。如果把 Integer 类型扩展到其他类型,那么 if…else 结构里的类型转换就可以省掉了。

switch 的改进

Goetz 和 Bierman 解释说,“switch 语句就是一种最完美的模式匹配”。例如:

复制代码
String formatted;switch (obj) {
case Integer i: formatted = String.format("int %d", i); break;
case Byte b: formatted = String.format("byte %d", b); break;
case Long l: formatted = String.format("long %d", l); break;
case Double d: formatted = String.format(“double %f", d); break;
default: formatted = String.format("String %s", s);
}...
{1}

上面的代码清晰易懂。不过,Goetz 和 Bierman 也指出了 switch 的一个局限——“它只是一个语句,所以分支也必须是语句。我们希望可以把它们变成三元操作符那样的表达式,这样就可以保证只对其中的一个表达式求值”。

他们建议引入一种新的表达式语句——exprswtich。

复制代码
String formatted =
exprswitch (obj) {
case Integer i -> String.format("int %d", i);
case Byte b -> String.format("byte %d", b);
case Long l -> String.format("long %d", l);
case Double d -> String.format(“double %f", d);
default -> String.format("String %s", s);
};
...
{1}

Goetz 和 Bierman 建议的模式如下所述。

  • 类型检测模式(将被转型的目标绑定到变量)
  • 解构模式(解构目标并进行递归匹配)
  • 常量模式(等值匹配)
  • 变量模式(任意匹配并绑定目标)
  • 下划线模式(任意匹配)

以下是 Goetz 与 InfoQ 的谈话内容。

InfoQ:在你发布论文后,社区都有哪些反馈?

Goetz:我们收到了非常积极的反馈。在其他语言里使用过模式匹配的人都很喜欢这个特性,他们也希望能够在 Java 中使用它。对于那些之前没有使用模式匹配的人,我们希望他们能够学会使用这个特性,我们认为很有必要在 Java 里添加这一特性。

InfoQ:Scala 的匹配操作符对 Java 模式匹配的设计有多大的影响?有什么事情是 Scala 的匹配操作能做的而 Java 却做不到的吗?

Goetz:Scala 只是众多启发我们在 Java 中加入模式匹配的语言之一。为一门语言添加特性不外乎从其他语言那里“移植”,但实际上,我们并不希望做得跟 Scala 完全一样,我们只要能够做到 Scala 的一部分,同时也能做 Scala 做不到的。

我们认为我们更有可能将模式匹配深度集成到对象模型中,比 Scala 有过之而无不及。Scala 的模式是静态的,难以重载或覆盖。虽说能够做到这样已经很好了,但我们希望能够做得更好。

解构(deconstruction)是构造(construction)的另一面,面向对象编程语言让我们可以构造对象(构造器、工厂、构建器),而解构将给我们带来更丰富的 API。虽说模式匹配与面向函数语言有一定的历史渊源,但我们相信它在面向对象语言里将会得到更好的发扬。

人们对语言特性津津乐道,不过我们认为语言特性真正的作用应该是为软件库提供更好的服务,而模式匹配将帮助我们写出更简单、更安全的软件库。

以 java.lang.Class 的两个方法为例:

复制代码
public boolean isArray() { ... }
public Class getComponentType() { ... }

第二个方法需要以第一个方法返回 true 作为前提。对于 API 提供者(需要些更多代码,也需要更多的测试)和用户(容易出错)来说,涉及多 API 的逻辑操作就意味着复杂性。从逻辑上看,这两个方法就是一个模式,融合了“判断这个类是否是一个数组类”和“根据条件抽取组件类型”。如果能够通过以下的表达式来表达,那么代码写起来就更简单了,而且不容易出错。

if (aClass matches Class.arrayClass(var componentType)) { ... }

InfoQ:这次是否把让 Scala rebase 模式匹配作为目标(比如 Scala 2.12 就基于接口对 trait 进行了 rebase)?

Goetz:与 Lambda 表达式一样,我们希望在设计这一语言特性的过程中,能够找到一些构建块,并把它们集成到底层的平台中,让其他语言也能从中获益,并为多个语言提供更好的互操作性。

InfoQ:Scala 在实现这一特性时添加了很多额外的字节码用于支持解构 case 类型,这样会造成负面影响吗?

Goetz:这样做最大的问题在于,编译器不再只是个编译器了,它往类成员里添加了语义。虽然这样做很方便,但可能不是用户想要的,比如,比较数组要用 Arrays.equals(),而不能用 Object.equals()。

InfoQ:解构是否仅限于数据类(data class)?

Goetz:我们计划分几次来发布模式匹配功能,最开始先发布类型检测,然后是数据类的解构模式,最后是用户自定义的解构模式。虽说解构不会仅限于数据类,但这一过程还是需要一些时间。

InfoQ:你能够解释一下数据类和值类型(value type)之间的关系吗?

Goetz:它们之间是一种正交关系。值就是一种合体,没有标识。通过显式地拒用标识,运行时可以优化内存布局,扁平化对象头部,更自由地跨同步点缓存值组件。数据类简化了类表示和 API 协定之间复杂的关系,编译器就可以注入常用的类成员,如构造器、模式匹配器、equals 方法、hashCode 方法和 toString 方法。有些类可以被声明成值类(value class),有些则适合被声明成数据类,或者都不声明,或者都声明,这些情况都有可能。

InfoQ:Sealing 特性是否需要源码编译器的支持?

Goetz:Sealing 特性不仅仅需要编译器的支持,也需要 JVM 的支持,这样语言层面的约束——比如“X 不能继承 Y”——就可以在 JVM 层面得到加强。

InfoQ:Sealing 是否意味着“只能在当前模块内扩展出子类”?

Goetz:Sealing 可以有多种说法,最简单的就是“这个类只能在同一个源码文件中被扩展”——这是最常见的情况,也是最简单的。Sealing 也可以被定义成“同一个包中”或“同一个模块中”,甚至可以是“友联(friend)”或复杂的运行时判断。

InfoQ:Java 的新发布周期有助于模式匹配被集成到 Java 中吗?

Goetz:我们希望如此。我们已经将模式匹配分为几个小块,这样就可以快速地推出最简单的部分,然后继续开发其他部分。

InfoQ:什么时候可以看到原型?

Goetz:现在就有了,尝鲜者可以直接从源代码编译 JDK。“ Amber ”上有一个分支已经可以支持类型检测模式和“matches”判断谓语。

InfoQ:你们将会怎样继续关于模式匹配的研究工作?

Goetz:我们会继续探究如何将匹配器作为类成员,以及如何实现重载和继承。我们还有很多工作要做。

更多资源

查看英文原文: Brian Goetz Speaks to InfoQ on Pattern Matching for Java

公众号推荐:

2024 年 1 月,InfoQ 研究中心重磅发布《大语言模型综合能力测评报告 2024》,揭示了 10 个大模型在语义理解、文学创作、知识问答等领域的卓越表现。ChatGPT-4、文心一言等领先模型在编程、逻辑推理等方面展现出惊人的进步,预示着大模型将在 2024 年迎来更广泛的应用和创新。关注公众号「AI 前线」,回复「大模型报告」免费获取电子版研究报告。

AI 前线公众号
2017-10-08 19:003096
用户头像

发布了 322 篇内容, 共 133.5 次阅读, 收获喜欢 142 次。

关注

评论

发布
暂无评论
发现更多内容

从0制作一个web端网易云

格斗家不爱在外太空沉思

vue.js axios 11月月更

披荆斩棘成功上岸美团、字节、华为,分享Java面经及答案

程序知音

Java java面试 后端技术 Java面试八股文

设计模式之美-面向对象

GalaxyCreater

设计模式

数据库系统的组成

阿泽🧸

数据库 11月月更

SAP UI5 barcode 控件的 feature 检查探测机制单步调试 - checkCordovaInIframe

Jerry Wang

JavaScript Fiori SAP UI5 ui5 11月月更

华为开发者大会HDC2022:HMS Core 持续创新,与开发者共创美好数智生活

HMS Core

HMS Core

为了面试字节,熬夜肝完这份Redis笔记后,我终于“硬”了一回

小小怪下士

Java redis 程序员 面试

漏洞扫描的种类

穿过生命散发芬芳

漏洞扫描 11月月更

2022-11-06:给定平面上n个点,x和y坐标都是整数, 找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的。 返回最短距离,精确到小数点后面4位。

福大大架构师每日一题

算法 rust 福大大

第九期-模块五

wuli洋

使用TSDB自动检测时序数据的异常情况

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

极客时间架构训练营模块四作业

李晨

架构

零基础入门网络安全,收藏这篇不迷茫【2022最新】

网络安全学海

黑客 网络安全 信息安全 渗透测试 WEB安全

Github已经54k个star的Docker,到底是什么?

Jackpop

架构实战营模块四

Geek_408c99

【云原生】Nacos-TaskManager 任务管理的使用

石臻臻的杂货铺

云原生 nacos 11月月更

纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码(简易版)

肥晨

11月月更 跳动的爱心 代码爱心 爱心代码

uniapp-如何在邀请页面生成海报

格斗家不爱在外太空沉思

vue.js uniapp 11月月更

小白入门:什么是CURD?

wljslmz

数据库 sql crud 11月月更

如何在论文中画出漂亮的插图?

Jackpop

Java引用类型(class、interface)用法总结详解

共饮一杯无

Java 11月月更 Java引用类型

强引用、软引用、弱引用、幻象引用有什么区别和使用场景

共饮一杯无

Java 引用类型 11月月更

Vue-VueRouter使用

格斗家不爱在外太空沉思

vue.js VueRouter 11月月更

【愚公系列】2022年11月 微信小程序-app.json配置属性

愚公搬代码

11月月更

架构误区系列1:简单依靠扩容解决容量问题

agnostic

架构误区

设计千万级学生管理系统的考试试卷存储方案

小虎

架构训练营

对象可达性状态流转分析、显式地影响软引用垃圾收集

共饮一杯无

Java 11月月更 引用类型扩展

HEM亮相华为HDC2022开发者大会,助力企业数字化办公

最新动态

Verilog语法入门

攻城狮Wayne

Verilog 11月月更 组合逻辑电路

架构误区系列(Architecture Pitfall)

agnostic

构架师

Go语言入门14—Channel

良猿

Go golang 后端 11月月更

与Brian Goetz聊Java的模式匹配_Java_Michael Redlich_InfoQ精选文章