与Brian Goetz聊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); }...

只有当变量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); }...

上面的代码清晰易懂。不过,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); };...

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

更多资源
Towards Pattern Matching in Java by Kerflyn, May 9, 2012 Pattern Matching in Java by Benji Weber, May 3, 2014 Pattern Matching in Java with the Visitor Pattern by Kevin Peterson, February 11, 2015 Adventures in Pattern Matching by Brian Goetz, JVM Language Summit, August 2017 Moving Java Faster by Mark Reinhold, September 6, 2017 Java to Move to 6-Monthly Release Cadence by InfoQ, September 6, 2017

本文转自d1net(转载)

相关文章
|
5月前
|
Java 应用服务中间件 Docker
java-web部署模式概述
本文总结了现代 Web 开发中 Spring Boot HTTP 接口服务的常见部署模式,包括 Servlet 与 Reactive 模型、内置与外置容器、物理机 / 容器 / 云环境部署及单体与微服务架构,帮助开发者根据实际场景选择合适的方案。
192 25
|
5月前
|
存储 Java 大数据
Java 大视界 -- Java 大数据在智能家居能源消耗模式分析与节能策略制定中的应用(198)
简介:本文探讨Java大数据技术在智能家居能源消耗分析与节能策略中的应用。通过数据采集、存储与智能分析,构建能耗模型,挖掘用电模式,制定设备调度策略,实现节能目标。结合实际案例,展示Java大数据在智能家居节能中的关键作用。
|
7月前
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
412 23
|
设计模式 XML 存储
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
文章详细介绍了工厂方法模式(Factory Method Pattern),这是一种创建型设计模式,用于将对象的创建过程委托给多个工厂子类中的某一个,以实现对象创建的封装和扩展性。文章通过日志记录器的实例,展示了工厂方法模式的结构、角色、时序图、代码实现、优点、缺点以及适用环境,并探讨了如何通过配置文件和Java反射机制实现工厂的动态创建。
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
|
设计模式 XML Java
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
文章详细介绍了简单工厂模式(Simple Factory Pattern),这是一种创建型设计模式,用于根据输入参数的不同返回不同类的实例,而客户端不需要知道具体类名。文章通过图表类的实例,展示了简单工厂模式的结构、时序图、代码实现、优缺点以及适用环境,并提供了Java代码示例和扩展应用,如通过配置文件读取参数来实现对象的创建。
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
249 11
|
消息中间件 Java
【实战揭秘】如何运用Java发布-订阅模式,打造高效响应式天气预报App?
【8月更文挑战第30天】发布-订阅模式是一种消息通信模型,发送者将消息发布到公共队列,接收者自行订阅并处理。此模式降低了对象间的耦合度,使系统更灵活、可扩展。例如,在天气预报应用中,`WeatherEventPublisher` 类作为发布者收集天气数据并通知订阅者(如 `TemperatureDisplay` 和 `HumidityDisplay`),实现组件间的解耦和动态更新。这种方式适用于事件驱动的应用,提高了系统的扩展性和可维护性。
287 2
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
366 2
|
设计模式 Java
Java设计模式-工厂方法模式(4)
Java设计模式-工厂方法模式(4)
140 0
|
设计模式 监控 Java
Java中的并发编程模式与最佳实践
随着多核处理器的普及,充分利用并发和多线程成为提高软件性能的关键。Java语言通过其丰富的并发API提供了强大的支持,使得开发者能够构建高效、可靠的并发应用程序。本文深入探讨了Java并发编程的核心概念、设计模式以及在实际开发中的最佳实践,旨在帮助读者更好地理解和掌握Java并发编程,从而编写出更加高效、稳定的应用程序。