详解Java19中的记录类型的模式匹配
Java19将于2022年9月20日发布,目前已经有发布候选(ReleaseCandidate,RC)版本可供下载。本文对Java19中的记录类型的模式匹配(RecordPatterns)进行介绍。在阅读本文之前,你可以参考另外三篇相关的文章:详解Java17中的记录类型(Record)详解Java17中的模式匹配(PatternMatching)详解Java19中switch的模式匹配
记录类型的模式是Java19中可以使用的一种新的匹配模式。记录类型的模式不能单独使用,而是要与instanceof或switch模式匹配一同使用。记录类型的模式主要用于简化下面的一类使用场景。
记录类型的模式是预览功能,需要通过javac和java命令的参数enablepreview来启用。
以记录类型GeoLocation为例来进行说明,要执行的操作仍然是进行格式化。publicrecordGeoLocation(doublelng,doublelat){}
期望的GeoLocation的格式化输出结果是类似〔lng,lat〕。传统的写法如下面的代码所示,先进行类型判断,然后再强制类型转换,最后使用GeoLocation提供的访问方法(lng()和lat())来获取值。if(objinstanceofGeoLocation){GeoLocationgeoLocation(GeoLocation)obj;returnString。format(〔。6f,。6f〕,geoLocation。lng(),geoLocation。lat());}
这种做法无疑是很繁琐的。记录类型的模式可以简化这种类型的操作,允许直接提取记录类型中的组件的值。
结合在详解Java17中的模式匹配(PatternMatching)一文中提到的模式的组成部分,记录类型的模式由下面两个部分组成:记录类型的模式的predicate与instanceof模式是一样的,都是进行类型匹配。记录类型的模式的模式变量的集合,包含了要提取的记录类型的组件,以及记录对象本身。
下面的代码展示了记录类型的模式在switch语句中的用法。GeoLocation(doublelng,doublelat)表示的是记录类型的模式,其中GeoLocation是需要匹配的类型,lng和lat是两个模式变量,表示从记录类型中提取的值。这两个模式变量可以直接使用。publicclassObjectFormatter{publicStringformat(Objectobj){returnswitch(obj){casenull;caseGeoLocation(doublelng,doublelat)String。format(〔。6f,。6f〕,lng,lat);casedefaultobj。toString();};}}
这里有几点需要注意:在声明模式变量时,并不需要显式地指定类型,用var也是可以的,如GeoLocation(varlng,varlat)。具体的类型由编译器自动推断。模式变量的名称不需要与记录类型的组件名称保持一致,如GeoLocation(varx,vary)也是可以的。这些变量是按照记录类型的组件列表中的位置来匹配的。
由于记录类型是可以嵌套的,在匹配时也同样可以进行嵌套,从而很方便地从复杂的对象层次结构中提取数据。
下面的代码使用了详解Java17中的记录类型(Record)一文中介绍的Order记录类型。记录类型的模式匹配时,直接访问了嵌套的Address记录的addressLine组件。publicstaticvoiddisplay(Orderorder){if(orderinstanceofOrder(StringorderId,StringuserId,LocalDateTimecreatedAt,ListOrder。LineItemlineItems,Order。Address(StringaddressLine,StringcityId,StringprovinceId,StringzipCode))){System。out。println(addressLine);}}
除了记录类型中的组件之外,记录对象本身也可以作为模式变量。在下面的代码中,GeoLocation(doublelng,doublelat)之后的g同样是模式变量,表示当前的GeoLocation对象。if(geoLocationinstanceofGeoLocation(doublelng,doublelat)g){System。out。println(g);System。out。println(lng,lat);}
使用泛型的记录类型也是可以的。在进行匹配时,必须使用带实际类型的泛型形式。比如,下面代码中的记录类型Box。recordBoxT(Tt){}
在下面的代码中,匹配的模式必须是类似Box这样的。staticvoidtest(BoxObjectbox){if(boxinstanceofBoxObject(Strings)){System。out。println(字符串:s);}}