JDK21新特性Record Patterns记录模式详解

1 摘要

通过使用记录模式来增强Java编程语言,以解构记录值。记录模式和类型模式可嵌套使用,从而实现强大、声明式和可组合的数据导航和处理形式。

2 发展史

由 JEP 405 提出的预览功能,并在JDK 19发布,然后由 JEP 432 再次预览,并在JDK 20发布。该功能与用于switch的模式匹配(JEP 441)共同演进,并且二者有相当大的交互作用。本JEP提议在持续的经验和反馈基础上对该功能完善。

除了一些次要的编辑更改,自第二个预览版以来的主要变化是删除了对增强for语句头部出现记录模式的支持。这个功能可能会在未来的JEP中重提。

3 目标

  • 扩展模式匹配以解构记录类的实例,实现更复杂的数据查询
  • 添加嵌套模式,实现更可组合的数据查询

4 动机

Java 16中, JEP 394 扩展了instanceof运算符,使其可接受类型模式并执行模式匹配。这个简单的扩展使得熟悉的instanceof和强制转换惯用法变得更简洁、更不易出错:

// <Java 16
if (obj instanceof String) {String s = (String)obj;... 使用s ...
}
// ≥Java 16
if (obj instanceof String s) {... 使用s ...
}

新代码中,若obj在运行时是String的实例,则obj与类型模式String s匹配。若模式匹配成功,则instanceof true,且模式变量s被初始化为obj强制转换为String的值,然后可以在包含的代码块中使用。

类型模式一次性消除了许多类型转换的出现。然而,它们只是朝着更声明式、以数据为焦点的编程风格迈出的第一步。随Java支持新的、更具表现力的数据建模,模式匹配可通过让开发表达模型的语义意图来简化对这些数据的使用。

5 Pattern matching和records

记录 (JEP 395) 是数据的透明载体。接收记录类实例的代码通常会使用内置的组件访问器方法提取数据,即组件。

5.1 Point的实例

如用类型模式测试一个值是否是记录类Point的实例,并在匹配成功时从该值中提取x和y组件。

Java8
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public int getY() {return y;}
}static void printSum(Object obj) {if (obj instanceof Point) {Point p = (Point) obj;int x = p.getX();int y = p.getY();System.out.println(x + y);}
}
≥Java 16
record Point(int x, int y) {}static void printSum(Object obj) {if (obj instanceof Point p) {int x = p.x();int y = p.y();System.out.println(x+y);}
}

仅使用模式变量p调用访问方法x()、y(),这些方法返回组件x和y的值。

在每个记录类中,其访问方法和组件之间存在一对一对应关系。

如果模式不仅可测试一个值是否是Point的实例,还可直接从该值中提取x和y组件,从而代表我们调用访问器方法的意图将更好。换句话说:

// Java 21及以后
static void printSum(Object obj) {if (obj instanceof Point(int x, int y)) {System.out.println(x+y);}
}

Point(int x, int y) 是一个record pattern。它将用于提取组件的局部变量的声明直接提升到模式本身,并在值与模式匹配时通过调用访问方法对这些变量初始化。实际上,record pattern将记录的实例解构为其组件。

6 嵌套record pattern

模式匹配的真正威力在于优雅扩展到匹配更复杂的对象图。

考虑以下声明:

// Java 16及以后
record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

已知可使用记录模式提取对象的组件。如想从左上角点提取颜色:

// Java 21及以后
static void printUpperLeftColoredPoint(Rectangle r) {if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) {System.out.println(ul.c());}
}

但ColoredPoint值ul本身是个记录值,希望进一步分解。因此,记录模式支持嵌套,允许对记录组件进一步匹配、分解。可在记录模式中嵌套另一个模式,同时对外部和内部记录分解:

// Java 21及以后
static void printColorOfUpperLeftPoint(Rectangle r) {if (r instanceof Rectangle(ColoredPoint(Point p, Color c),ColoredPoint lr)) {System.out.println(c);}
}

嵌套模式允许以与组装对象的代码一样清晰简洁方式拆解聚合。如创建一个矩形,通常会将构造函数嵌套在一个表达式中:

// Java 16及以后
Rectangle r = new Rectangle(new ColoredPoint(new Point(x1, y1), c1), new ColoredPoint(new Point(x2, y2), c2));

使用嵌套模式,我们可以使用与嵌套构造函数结构相似的代码来解构这样的矩形:

// Java 21及以后
static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) {if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c),var lr)) {System.out.println("Upper-left corner: " + x);}
}

嵌套模式可能无法匹配:

// Java 21及以后
record Pair(Object x, Object y) {}
Pair p = new Pair(42, 42);
if (p instanceof Pair(String s, String t)) {System.out.println(s + ", " + t);
} else {System.out.println("Not a pair of strings");
}

这里的记录模式Pair(String s, String t)包含了两个嵌套的类型模式,即String s和String t。如果一个值与模式Pair(String s, String t)匹配,那么它是一个Pair,并且递归地,它的组件值与类型模式String s和String t匹配。在我们上面的示例代码中,由于记录的两个组件值都不是字符串,因此这些递归的模式匹配失败,因此执行else块。

总之,嵌套模式消除了导航对象的意外复杂性,使我们能专注这些对象所表示的数据。它们还赋予我们集中处理错误的能力,因为如果一个值无法与嵌套模式P(Q)匹配,那子模式P和Q中的任何一个或两个都无法匹配。我们不需要检查和处理每个单独的子模式匹配失败——要么整个模式匹配,要么不匹配。

7 描述

使用可嵌套的记录模式。

模式语法变为:

Pattern:TypePatternRecordPatternTypePattern:LocalVariableDeclarationRecordPattern:ReferenceType ( [ PatternList ] )PatternList: Pattern { , Pattern }

8 记录模式

由记录类类型和(可能为空的)模式列表组成,该列表用于与相应的记录组件值进行匹配。

如声明

record Point(int i, int j) {}

如果值v与记录模式Point(int i, int j)匹配,则它是记录类型Point的实例;如这样,模式变量i将被初始化为在值v上调用与i对应的访问器方法的结果,模式变量j将被初始化为在值v上调用与j对应的访问器方法的结果。(模式变量的名称不需要与记录组件的名称相同;也就是说,记录模式Point(int x, int y)的行为相同,只是模式变量x和y被初始化。)

null值不与任何记录模式匹配。

记录模式可用var来匹配记录组件,而无需声明组件的类型。在这种情况下,编译器会推断由var模式引入的模式变量的类型。如模式Point(var a, var b)是模式Point(int a, int b)的简写。

记录模式声明的模式变量集合包括模式列表中声明的所有模式变量。

如果一个表达式可以在不需要未经检查的转换的情况下将其转换为模式中的记录类型,则该表达式与记录模式兼容。

如果记录模式命名了一个泛型记录类,但没有给出类型参数(即,记录模式使用原始类型),则始终会推断类型参数。例如:

// Java 21及以后
record MyPair<S,T>(S fst, T snd){};
static void recordInference(MyPair<String, Integer> pair){switch (pair) {case MyPair(var f, var s) -> ... // 推断的记录模式 MyPair<String,Integer>(var f, var s)...}
}

记录模式的类型参数推断在支持记录模式的所有结构中都受到支持,即instanceof表达式和switch语句和表达式。

推断适用于嵌套记录模式;例如:

// Java 21及以后
record Box<T>(T t) {}
static void test1(Box<Box<String>> bbs) {if (bbs instanceof Box<Box<String>>(Box(var s))) {System.out.println("String " + s);}
}

这里,嵌套模式Box(var s)的类型参数被推断为String,因此模式本身被推断为Box(var s)。

甚至可省略外部记录模式中的类型参数,得到简洁代码:

// Java 21及以后
static void test2(Box<Box<String>> bbs) {if (bbs instanceof Box(Box(var s))) {System.out.println("String " + s);}
}

这里编译器会推断整个instanceof模式为Box<Box<String>>(Box<String>(var s))

为保持兼容性,类型模式不支持隐式推断类型参数;如类型模式List l始终被视为原始类型模式。

9 记录模式和完整的switch

JEP 441增强了switch表达式和switch语句,以支持模式标签。无论是switch表达式还是模式switch语句,都必须是完整的:switch块必须有处理选择器表达式的所有可能值的子句。对于模式标签,这是通过分析模式的类型来确定的;例如,case标签case Bar b匹配类型为Bar及其所有可能的子类型的值。

对于涉及记录模式的模式标签,分析更加复杂,因为我们必须考虑组件模式的类型,并对密封层次结构进行调整。例如,考虑以下声明:

class A {}
class B extends A {}
sealed interface I permits C, D {}
final class C implements I {}
final class D implements I {}
record Pair<T>(T x, T y) {}
Pair<A> p1;
Pair<I> p2;

以下switch不是完整的,因为没有匹配包含两个类型为A的值的对:

// Java 21及以后
switch (p1) {                 // 错误!case Pair<A>(A a, B b) -> ...case Pair<A>(B b, A a) -> ...
}

这两个switch是完整的,因为接口I是密封的,因此类型C和D涵盖了所有可能的实例:

// Java 21及以后
switch (p2) {case Pair<I>(I i, C c) -> ...case Pair<I>(I i, D d) -> ...
}switch (p2) {case Pair<I>(C c, I i) -> ...case Pair<I>(D d, C c) -> ...case Pair<I>(D d1, D d2) -> ...
}

相比之下,这个switch不是完整的,因为没有匹配包含两个类型为D的值的对:

// Java 21及以后
switch (p2) {                        // 错误!case Pair<I>(C fst, D snd) -> ...case Pair<I>(D fst, C snd) -> ...case Pair<I>(I fst, C snd) -> ...
}

10 未来

记录模式的描述中提到了许多可以扩展这里描述的记录模式的方向:

  • 可变参数模式,用于可变数量的记录
  • 匿名模式,可以出现在记录模式的模式列表中,匹配任何值,但不声明模式变量
  • 适用于任意类的值而不仅仅是记录类的模式。

我们可以在未来的JEP中考虑其中的一些方向。

11 依赖关系

本JEP建立在Pattern Matching for instanceof(JEP 394)的基础上,该功能已在JDK 16中发布。它与Pattern Matching for switch(JEP 441)共同演进。

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/87993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitLab数据迁移后出现500错误

一、背景 去年做GitLab数据迁移时&#xff0c;写过一篇文章《GitLab的备份与还原》。后来发现新创建的项目没问题&#xff0c;但对于迁移过来的项目&#xff0c;修改名称等信息&#xff0c;或者删除该项目时&#xff0c;会出现500错误&#xff0c;以为是系统问题&#…

websocket请求通过IteratorAggregate实现流式输出

对接国内讯飞星火模型&#xff0c;官方文档接口采用的是websocket跟国外chatgpt有些差异。 虽然官网给出一个简单demo通过while(true)&#xff0c;websocket的receive()可以实现逐条接受并输出给前端&#xff0c;但是通用和灵活度不高。不能兼容现有项目框架的流式输出。故模仿…

安卓Compose(一)

为什么学习安卓Compose&#xff1f; 安卓Compose是一个相对新的UI工具包&#xff0c;它的出现为安卓应用程序开发带来了一系列的好处。下面是一些学习Compose的理由&#xff1a; 声明式UI 与传统的安卓XML布局相比&#xff0c;Compose使用了声明式的UI编程范例。这意味着你可以…

Verilog零基础入门(边看边练与测试仿真)-状态机-笔记(7-10讲)

文章目录 第七讲第八讲第九讲第十讲 第七讲 1、最简单的状态机-三角波发生器 1、两种状态的代码&#xff1a; //最简单的状态机&#xff0c;三角波发生器&#xff1b; timescale 1ns/10ps module tri_gen(clk,res,d_out); input clk; input res; o…

小程序搜索词排名优化的诀窍

随着小程序的普及,如何提高小程序在搜索结果中的排名也变得重要。优化小程序搜索词排名可以扩大用户流量,提高曝光度。那么,小程序搜索词排名优化需要注意哪些方面呢?下面我就结合自己的经验,和大家分享些实用技巧。【名即薇】 首先,选择合适的搜索词非常关键。目标是找到既符…

C语言 宏定义使用方式

在C语言中&#xff0c;宏定义是一种预处理指令&#xff0c;用于为代码创建别名或常量。它使用#define关键字来定义宏。 以下是宏定义的使用方式&#xff1a; 1)简单的宏定义&#xff1a; #define PI 3.14159这将为数值3.14159定义一个名为PI的宏。在代码中&#xff0c;每次出…

【Linux】【网络】传输层协议:TCP

文章目录 TCP 协议1. TCP 协议段格式2. TCP 报头解析3. TCP 的可靠性4. 面向字节流5. 粘包问题6. 连接队列维护 TCP 的 确认应答机制TCP 的 超时重传机制TCP 的 三次握手TCP 的 四次挥手setsockopt 函数&#xff1a;设置套接字选项&#xff0c;解决 TIME_WAIT 状态引起的 bind …

在B站上如何把已经上传的视频做成合集?

参考视频: 【在B站上如何把已经上传的视频做成合集&#xff1f;】 https://www.bilibili.com/video/BV1Uf4y1G7eR/?share_sourcecopy_web&vd_source8af85e60c2df9af1f0fd23935753a933 【B站投稿视频合集的几种方式最全攻略】 https://www.bilibili.com/video/BV1jZ4y1h7…

SpringCloud 学习(三)Ribbon 和 Feign

4. Netflix.Ribbon 4.1 简介 (1) 概念 Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡工具。 (2) 负载均衡&#xff08;LB&#xff1a;LoadBalance&#xff09;和集群架构 应用集群&#xff1a;将同一应用部署到多台机器上&#xff0c;组成处理集群&…

Android逆向技术高阶大法

原文链接 Android逆向技术高阶大法 安卓应用是一个客户端&#xff0c;与传统软件类似&#xff0c;需要把软件打包&#xff0c;然后通过某种渠道&#xff08;应用市场&#xff09;分发给用户&#xff0c;这是常规的发布方式&#xff0c;它的更新节奏很慢&#xff0c;从你在应用…

maven settings.xml文件(包含了配置阿里云镜像)

mac 的 settings.xml 我配置的位置是&#xff1a; /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/conf/settings.xml 然后 local repository 我配置的位置是&#xff1a; /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/conf/repos…

Docker 容器监控之CAdvisor+InfluxDB+Granfana

是什么 一句话&#xff1a;CAdvisor监控收集InfluxDB存储数据Granfana展示图表 CAdvisor InfluxDB Granfana 总结 容器编排CIG CIG CAdvisorInfluxDBGranfana 1、新建目录 2、新建docker-compose.yml文件 version: 3.1volumes:grafana_data: {}services:influxdb:image: t…

C语言实现八种功能的通讯录(添加、删除、查找、修改、显示、排序、退出、清空)

通讯录功能概要及前提说明 此通讯录利用C语言完成&#xff0c;可以实现八种功能的通讯录&#xff08;添加、删除、查找、修改、显示、排序、退出、清空&#xff09; 代码由三部分组成&#xff0c;为什么要写成三部分而不写成一部分可以参考我以前的博客&#xff0c;如下&…

【PMP/软考】软件需求的三个主要层次:业务需求、用户需求和功能需求解释及实例解析

简述 当进行需求分析时&#xff0c;通常着重考虑三个主要层次&#xff1a;业务需求、用户需求和功能需求。业务需求关注项目与组织战略目标的一致性&#xff0c;用户需求明确最终用户的期望&#xff0c;而功能需求定义具体的系统功能和特性。这三个层次为项目管理和软件工程提…

C++ GetWindowText()用法

使用UpdateData()函数时&#xff0c;当前界面上所有绑定了的变量(即通过MFC ClassWizard给控件添加了对应 的变量)都会被UpdateData(TRUE)更新成对应控件中的内容&#xff1b;同样所有绑定了变量的控件中的内容也会 UpdateData(FALSE)更新成对应变量中的内容。 要接受用户的输…

JavaScript函数的增强知识

一、函数属性和arguments 1.函数对象的属性 我们知道JavaScript中函数也是一个对象&#xff0c;那么对象中就可以有属性和方法。 属性name&#xff1a;一个函数的名词我们可以通过name来访问&#xff1b; function foo() {} console.log(foo.name);// foovar bar function…

L1-018 大笨钟

L1-018 大笨钟 微博上有个自称“大笨钟V”的家伙&#xff0c;每天敲钟催促码农们爱惜身体早点睡觉。不过由于笨钟自己作息也不是很规律&#xff0c;所以敲钟并不定时。一般敲钟的点数是根据敲钟时间而定的&#xff0c;如果正好在某个整点敲&#xff0c;那么“当”数就等于那个整…

使用koajs,在db.query中ctx.body.res,前端收到NOT FOUND的解决办法

1.看来很多帖子&#xff0c;出现这种原因好像是由于koajs的异步同步问题&#xff0c;我们只需为其添加promise&#xff0c;然后使用resolve返回数据即可 2.代码如下&#xff1a; const getUserMenu (ctx, next) > {return new Promise((resolve, reject) > {const use…

基于改进莱维飞行和混沌映射的粒子群优化BP神经网络预测股票价格研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

[架构之路-223]:数据管理能力成熟度评估模型DCMM简介

目录 一、背景 二、评估依据 三、评估内容 四、主要适用对象 五、能力等级 六、不同层次的文件&#xff1a; 一、背景 信息技术与经济社会的交汇融合引发了数据爆发式增长。数据蕴含着重要的价值&#xff0c;已成为国家基础性战略资源&#xff0c;正日益对全球生产、流通…